1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software Foundation,
21    51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22 
23 #include "sql_base.h"                   /* open_normal_and_derived_tables */
24 #include "sql_table.h"                  /* build_table_filename */
25 #include "sql_show.h"                   /* append_identifier */
26 #include "sql_view.h"                   /* VIEW_ANY_ACL */
27 #include "rpl_filter.h"                 /* rpl_filter */
28 #include "sql_parse.h"                  /* get_current_user */
29                                         /* any_db */
30 #include "binlog.h"                     /* mysql_bin_log */
31 #include "sp.h"                         /* sp_exist_routines */
32 #include "sql_insert.h"                 /* Sql_cmd_insert_base */
33 #include "log.h"                        /* sql_print_warning */
34 
35 #include "sql_update.h"
36 #include "auth_internal.h"
37 #include "sql_auth_cache.h"
38 #include "sql_authentication.h"
39 #include "sql_authorization.h"
40 #include "debug_sync.h"
41 #include "sql_user_table.h"
42 
43 const char *command_array[]=
44 {
45   "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
46   "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
47   "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
48   "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
49   "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
50   "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE"
51 };
52 
53 uint command_lengths[]=
54 {
55   6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
56   14, 13, 11, 5, 7, 17
57 };
58 
59 const char *any_db="*any*";	// Special symbol for check_access
60 
61 
62 static bool check_show_access(THD *thd, TABLE_LIST *table);
63 
64 /**
65   Get a cached internal schema access.
66   @param grant_internal_info the cache
67   @param schema_name the name of the internal schema
68 */
69 const ACL_internal_schema_access *
get_cached_schema_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name)70 get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
71                          const char *schema_name)
72 {
73   if (grant_internal_info)
74   {
75     if (! grant_internal_info->m_schema_lookup_done)
76     {
77       grant_internal_info->m_schema_access=
78         ACL_internal_schema_registry::lookup(schema_name);
79       grant_internal_info->m_schema_lookup_done= TRUE;
80     }
81     return grant_internal_info->m_schema_access;
82   }
83   return ACL_internal_schema_registry::lookup(schema_name);
84 }
85 
86 
87 /**
88   Get a cached internal table access.
89   @param grant_internal_info the cache
90   @param schema_name the name of the internal schema
91   @param table_name the name of the internal table
92 */
93 const ACL_internal_table_access *
get_cached_table_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name,const char * table_name)94 get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
95                         const char *schema_name,
96                         const char *table_name)
97 {
98   assert(grant_internal_info);
99   if (! grant_internal_info->m_table_lookup_done)
100   {
101     const ACL_internal_schema_access *schema_access;
102     schema_access= get_cached_schema_access(grant_internal_info, schema_name);
103     if (schema_access)
104       grant_internal_info->m_table_access= schema_access->lookup(table_name);
105     grant_internal_info->m_table_lookup_done= TRUE;
106   }
107   return grant_internal_info->m_table_access;
108 }
109 
110 
111 ACL_internal_access_result
check(ulong want_access,ulong * save_priv) const112 IS_internal_schema_access::check(ulong want_access,
113                                  ulong *save_priv) const
114 {
115   want_access &= ~SELECT_ACL;
116 
117   /*
118     We don't allow any simple privileges but SELECT_ACL on
119     the information_schema database.
120   */
121   if (unlikely(want_access & DB_ACLS))
122     return ACL_INTERNAL_ACCESS_DENIED;
123 
124   /* Always grant SELECT for the information schema. */
125   *save_priv|= SELECT_ACL;
126 
127   return want_access ? ACL_INTERNAL_ACCESS_CHECK_GRANT :
128                        ACL_INTERNAL_ACCESS_GRANTED;
129 }
130 
131 const ACL_internal_table_access *
lookup(const char * name) const132 IS_internal_schema_access::lookup(const char *name) const
133 {
134   /* There are no per table rules for the information schema. */
135   return NULL;
136 }
137 
138 /**
139   Perform first stage of privilege checking for SELECT statement.
140 
141   @param thd          Thread context.
142   @param lex          LEX for SELECT statement.
143   @param tables       List of tables used by statement.
144   @param first_table  First table in the main SELECT of the SELECT
145                       statement.
146 
147   @retval FALSE - Success (column-level privilege checks might be required).
148   @retval TRUE  - Failure, privileges are insufficient.
149 */
150 
select_precheck(THD * thd,LEX * lex,TABLE_LIST * tables,TABLE_LIST * first_table)151 bool select_precheck(THD *thd, LEX *lex, TABLE_LIST *tables,
152                      TABLE_LIST *first_table)
153 {
154   bool res;
155   /*
156     lex->exchange != NULL implies SELECT .. INTO OUTFILE and this
157     requires FILE_ACL access.
158   */
159   ulong privileges_requested= lex->exchange ? SELECT_ACL | FILE_ACL :
160                                               SELECT_ACL;
161 
162   if (tables)
163   {
164     res= check_table_access(thd,
165                             privileges_requested,
166                             tables, FALSE, UINT_MAX, FALSE) ||
167          (first_table && first_table->schema_table_reformed &&
168           check_show_access(thd, first_table));
169   }
170   else
171     res= check_access(thd, privileges_requested, any_db, NULL, NULL, 0, 0);
172 
173   return res;
174 }
175 
176 
177 /**
178   Multi update query pre-check.
179 
180   @param thd		Thread handler
181   @param tables	Global/local table list (have to be the same)
182 
183   @retval
184     FALSE OK
185   @retval
186     TRUE  Error
187 */
188 
multi_update_precheck(THD * thd,TABLE_LIST * tables)189 bool Sql_cmd_update::multi_update_precheck(THD *thd, TABLE_LIST *tables)
190 {
191   DBUG_ENTER("multi_update_precheck");
192 
193   /*
194     Ensure that we have UPDATE or SELECT privilege for each table
195     The exact privilege is checked in mysql_multi_update()
196   */
197   for (TABLE_LIST *table= tables; table; table= table->next_global)
198   {
199     /*
200       "uses_materialization()" covers the case where a prepared statement is
201       executed and a view is decided to be materialized during preparation.
202     */
203     if (table->is_derived() || table->uses_materialization())
204       table->grant.privilege= SELECT_ACL;
205     else if ((check_access(thd, UPDATE_ACL, table->db,
206                            &table->grant.privilege,
207                            &table->grant.m_internal,
208                            0, 1) ||
209               check_grant(thd, UPDATE_ACL, table, FALSE, 1, TRUE)) &&
210              (check_access(thd, SELECT_ACL, table->db,
211                            &table->grant.privilege,
212                            &table->grant.m_internal,
213                            0, 0) ||
214               check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)))
215       DBUG_RETURN(TRUE);
216 
217     table->table_in_first_from_clause= 1;
218   }
219 
220   DBUG_RETURN(FALSE);
221 }
222 
223 
224 /**
225   Multi delete query pre-check.
226 
227   @param thd			Thread handler
228   @param tables		Global/local table list
229 
230   @retval
231     FALSE OK
232   @retval
233     TRUE  error
234 */
235 
multi_delete_precheck(THD * thd,TABLE_LIST * tables)236 bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
237 {
238   TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
239   TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
240   DBUG_ENTER("multi_delete_precheck");
241 
242   /* sql_yacc guarantees that tables and aux_tables are not zero */
243   assert(aux_tables != 0);
244   if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
245     DBUG_RETURN(TRUE);
246 
247   /*
248     Since aux_tables list is not part of LEX::query_tables list we
249     have to juggle with LEX::query_tables_own_last value to be able
250     call check_table_access() safely.
251   */
252   thd->lex->query_tables_own_last= 0;
253   if (check_table_access(thd, DELETE_ACL, aux_tables, FALSE, UINT_MAX, FALSE))
254   {
255     thd->lex->query_tables_own_last= save_query_tables_own_last;
256     DBUG_RETURN(TRUE);
257   }
258   thd->lex->query_tables_own_last= save_query_tables_own_last;
259 
260   DBUG_RETURN(FALSE);
261 }
262 
263 
264 /**
265   simple UPDATE query pre-check.
266 
267   @param thd		Thread handler
268   @param tables	Global table list
269 
270   @retval
271     FALSE OK
272   @retval
273     TRUE  Error
274 */
275 
update_precheck(THD * thd,TABLE_LIST * tables)276 bool Sql_cmd_update::update_precheck(THD *thd, TABLE_LIST *tables)
277 {
278   DBUG_ENTER("update_precheck");
279   const bool res= check_one_table_access(thd, UPDATE_ACL, tables);
280   DBUG_RETURN(res);
281 }
282 
283 
284 /**
285   simple DELETE query pre-check.
286 
287   @param thd		Thread handler
288   @param tables	Global table list
289 
290   @retval
291     FALSE  OK
292   @retval
293     TRUE   error
294 */
295 
delete_precheck(THD * thd,TABLE_LIST * tables)296 bool delete_precheck(THD *thd, TABLE_LIST *tables)
297 {
298   DBUG_ENTER("delete_precheck");
299   if (check_one_table_access(thd, DELETE_ACL, tables))
300     DBUG_RETURN(TRUE);
301   /* Set privilege for the WHERE clause */
302   tables->set_want_privilege(SELECT_ACL);
303   DBUG_RETURN(FALSE);
304 }
305 
306 
307 /**
308   simple INSERT query pre-check.
309 
310   @param thd		Thread handler
311   @param tables	Global table list
312 
313   @retval
314     FALSE  OK
315   @retval
316     TRUE   error
317 */
318 
insert_precheck(THD * thd,TABLE_LIST * tables)319 bool Sql_cmd_insert_base::insert_precheck(THD *thd, TABLE_LIST *tables)
320 {
321   LEX *lex= thd->lex;
322   DBUG_ENTER("insert_precheck");
323 
324   /*
325     Check that we have modify privileges for the first table and
326     select privileges for the rest
327   */
328   ulong privilege= (INSERT_ACL |
329                     (lex->duplicates == DUP_REPLACE ? DELETE_ACL : 0) |
330                     (insert_value_list.elements ? UPDATE_ACL : 0));
331 
332   if (check_one_table_access(thd, privilege, tables))
333     DBUG_RETURN(TRUE);
334 
335   DBUG_RETURN(FALSE);
336 }
337 
338 
339 /**
340   Check privileges for LOCK TABLES statement.
341 
342   @param thd     Thread context.
343   @param tables  List of tables to be locked.
344 
345   @retval FALSE - Success.
346   @retval TRUE  - Failure.
347 */
348 
lock_tables_precheck(THD * thd,TABLE_LIST * tables)349 bool lock_tables_precheck(THD *thd, TABLE_LIST *tables)
350 {
351   TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
352 
353   for (TABLE_LIST *table= tables; table != first_not_own_table && table;
354        table= table->next_global)
355   {
356     if (is_temporary_table(table))
357       continue;
358 
359     if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, table,
360                            FALSE, 1, FALSE))
361       return TRUE;
362   }
363 
364   return FALSE;
365 }
366 
367 
368 /**
369   CREATE TABLE query pre-check.
370 
371   @param thd			Thread handler
372   @param tables		Global table list
373   @param create_table	        Table which will be created
374 
375   @retval
376     FALSE   OK
377   @retval
378     TRUE   Error
379 */
380 
create_table_precheck(THD * thd,TABLE_LIST * tables,TABLE_LIST * create_table)381 bool create_table_precheck(THD *thd, TABLE_LIST *tables,
382                            TABLE_LIST *create_table)
383 {
384   LEX *lex= thd->lex;
385   SELECT_LEX *select_lex= lex->select_lex;
386   ulong want_priv;
387   bool error= TRUE;                                 // Error message is given
388   DBUG_ENTER("create_table_precheck");
389 
390   /*
391     Require CREATE [TEMPORARY] privilege on new table; for
392     CREATE TABLE ... SELECT, also require INSERT.
393   */
394 
395   want_priv= (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
396              CREATE_TMP_ACL :
397              (CREATE_ACL | (select_lex->item_list.elements ? INSERT_ACL : 0));
398 
399   if (check_access(thd, want_priv, create_table->db,
400                    &create_table->grant.privilege,
401                    &create_table->grant.m_internal,
402                    0, 0))
403     goto err;
404 
405   /* If it is a merge table, check privileges for merge children. */
406   if (lex->create_info.merge_list.first)
407   {
408     /*
409       The user must have (SELECT_ACL | UPDATE_ACL | DELETE_ACL) on the
410       underlying base tables, even if there are temporary tables with the same
411       names.
412 
413       From user's point of view, it might look as if the user must have these
414       privileges on temporary tables to create a merge table over them. This is
415       one of two cases when a set of privileges is required for operations on
416       temporary tables (see also CREATE TABLE).
417 
418       The reason for this behavior stems from the following facts:
419 
420         - For merge tables, the underlying table privileges are checked only
421           at CREATE TABLE / ALTER TABLE time.
422 
423           In other words, once a merge table is created, the privileges of
424           the underlying tables can be revoked, but the user will still have
425           access to the merge table (provided that the user has privileges on
426           the merge table itself).
427 
428         - Temporary tables shadow base tables.
429 
430           I.e. there might be temporary and base tables with the same name, and
431           the temporary table takes the precedence in all operations.
432 
433         - For temporary MERGE tables we do not track if their child tables are
434           base or temporary. As result we can't guarantee that privilege check
435           which was done in presence of temporary child will stay relevant later
436           as this temporary table might be removed.
437 
438       If SELECT_ACL | UPDATE_ACL | DELETE_ACL privileges were not checked for
439       the underlying *base* tables, it would create a security breach as in
440       Bug#12771903.
441     */
442 
443     if (check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
444                            lex->create_info.merge_list.first,
445                            FALSE, UINT_MAX, FALSE))
446       goto err;
447   }
448 
449   if (want_priv != CREATE_TMP_ACL &&
450       check_grant(thd, want_priv, create_table, FALSE, 1, FALSE))
451     goto err;
452 
453   if (select_lex->item_list.elements)
454   {
455     /* Check permissions for used tables in CREATE TABLE ... SELECT */
456     if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
457                                      UINT_MAX, FALSE))
458       goto err;
459   }
460   else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
461   {
462     if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
463       goto err;
464   }
465 
466   if (check_fk_parent_table_access(thd, create_table->db,
467                                    &lex->create_info, &lex->alter_info))
468     goto err;
469 
470   error= FALSE;
471 
472 err:
473   DBUG_RETURN(error);
474 }
475 
476 /**
477   @brief Performs standardized check whether to prohibit (TRUE)
478     or allow (FALSE) operations based on read_only and super_read_only
479     state.
480   @param thd              Thread handler
481   @param err_if_readonly  Boolean indicating whether or not
482     to add the error to the thread context if read-only is
483     violated.
484 
485   @returns Status code
486     @retval TRUE The operation should be prohibited.
487 @   retval FALSE The operation should be allowed.
488 */
check_readonly(THD * thd,bool err_if_readonly)489 bool check_readonly(THD *thd, bool err_if_readonly)
490 {
491   DBUG_ENTER("check_readonly");
492 
493   /* read_only=OFF, do not prohibit operation: */
494   if (!opt_readonly)
495     DBUG_RETURN(FALSE);
496 
497   /*
498     Thread is replication slave or skip_read_only check is enabled for the
499     command, do not prohibit operation.
500   */
501   if (thd->slave_thread || thd->is_cmd_skip_readonly())
502     DBUG_RETURN(FALSE);
503 
504   bool is_super = thd->security_context()->check_access(SUPER_ACL);
505 
506   /* super_read_only=OFF and user has SUPER privilege,
507   do not prohibit operation:
508   */
509   if (is_super && !opt_super_readonly)
510 
511     DBUG_RETURN(FALSE);
512 
513   /* throw error in standardized way if requested: */
514   if (err_if_readonly)
515     err_readonly(thd);
516 
517 
518   /* in all other cases, prohibit operation: */
519   DBUG_RETURN(TRUE);
520 }
521 
522 /**
523   @brief Generates appropriate error messages for read-only state
524     depending on whether user has SUPER privilege or not.
525 
526   @param thd              Thread handler
527 
528 */
err_readonly(THD * thd)529 void err_readonly(THD *thd)
530 {
531   my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
532     thd->security_context()->check_access(SUPER_ACL) ?
533     "--super-read-only" : "--read-only");
534 
535 }
536 
537 
538 #ifndef NO_EMBEDDED_ACCESS_CHECKS
539 
540 /**
541   Wrapper class which simplifies read guard usage for LOCK_grant.
542 */
543 class LOCK_grant_read_guard : public Partitioned_rwlock_read_guard
544 {
545 public:
LOCK_grant_read_guard(THD * thd)546   explicit LOCK_grant_read_guard(THD *thd)
547     : Partitioned_rwlock_read_guard(&LOCK_grant, thd->thread_id())
548   { }
549 };
550 
551 
552 /**
553   Check grants for commands which work only with one table and all other
554   tables belonging to subselects or implicitly opened tables.
555 
556   @param thd			Thread handler
557   @param privilege		requested privilege
558   @param all_tables		global table list of query
559 
560   @returns false on success, true on access denied error
561 */
562 
check_one_table_access(THD * thd,ulong privilege,TABLE_LIST * all_tables)563 bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
564 {
565   if (check_single_table_access(thd, privilege, all_tables, false))
566     return true;
567 
568   // Check privileges on tables from subqueries and implicitly opened tables
569   TABLE_LIST *subquery_table;
570   TABLE_LIST *const view= all_tables->is_view() ? all_tables : NULL;
571 
572   if ((subquery_table= all_tables->next_global))
573   {
574     /*
575       Access rights asked for the first table of a view should be the same
576       as for the view
577     */
578     if (view && subquery_table->belong_to_view == view)
579     {
580       if (check_single_table_access(thd, privilege, subquery_table, false))
581         return true;            /* purecov: inspected */
582       subquery_table= subquery_table->next_global;
583     }
584     if (subquery_table &&
585         check_table_access(thd, SELECT_ACL, subquery_table, false,
586                            UINT_MAX, false))
587       return true;
588   }
589   return false;
590 }
591 
592 
593 /**
594   Check grants for commands which work only with one table.
595 
596   @param thd                    Thread handler
597   @param privilege              requested privilege
598   @param all_tables             global table list of query
599   @param no_errors              FALSE/TRUE - report/don't report error to
600                             the client (using my_error() call).
601 
602   @retval
603     0   OK
604   @retval
605     1   access denied, error is sent to client
606 */
607 
check_single_table_access(THD * thd,ulong privilege,TABLE_LIST * all_tables,bool no_errors)608 bool check_single_table_access(THD *thd, ulong privilege,
609                                TABLE_LIST *all_tables, bool no_errors)
610 {
611   Security_context *backup_ctx= thd->security_context();
612 
613   /* we need to switch to the saved context (if any) */
614   if (all_tables->security_ctx)
615     thd->set_security_context(all_tables->security_ctx);
616 
617   const char *db_name;
618   if ((all_tables->is_view() || all_tables->field_translation) &&
619       !all_tables->schema_table)
620     db_name= all_tables->view_db.str;
621   else
622     db_name= all_tables->db;
623 
624   if (check_access(thd, privilege, db_name,
625                    &all_tables->grant.privilege,
626                    &all_tables->grant.m_internal,
627                    0, no_errors))
628     goto deny;
629 
630   /* Show only 1 table for check_grant */
631   if (!(all_tables->belong_to_view &&
632         (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
633       check_grant(thd, privilege, all_tables, FALSE, 1, no_errors))
634     goto deny;
635 
636   thd->set_security_context(backup_ctx);
637   return 0;
638 
639 deny:
640   thd->set_security_context(backup_ctx);
641   return 1;
642 }
643 
644 
645 bool
check_routine_access(THD * thd,ulong want_access,const char * db,char * name,bool is_proc,bool no_errors)646 check_routine_access(THD *thd, ulong want_access, const char *db, char *name,
647 		     bool is_proc, bool no_errors)
648 {
649   TABLE_LIST tables[1];
650 
651   memset(tables, 0, sizeof(TABLE_LIST));
652   tables->db= db;
653   tables->table_name= tables->alias= name;
654 
655   /*
656     The following test is just a shortcut for check_access() (to avoid
657     calculating db_access) under the assumption that it's common to
658     give persons global right to execute all stored SP (but not
659     necessary to create them).
660     Note that this effectively bypasses the ACL_internal_schema_access checks
661     that are implemented for the INFORMATION_SCHEMA and PERFORMANCE_SCHEMA,
662     which are located in check_access().
663     Since the I_S and P_S do not contain routines, this bypass is ok,
664     as long as this code path is not abused to create routines.
665     The assert enforce that.
666   */
667   assert((want_access & CREATE_PROC_ACL) == 0);
668   if (thd->security_context()->check_access(want_access))
669     tables->grant.privilege= want_access;
670   else if (check_access(thd, want_access, db,
671                         &tables->grant.privilege,
672                         &tables->grant.m_internal,
673                         0, no_errors))
674     return TRUE;
675 
676   return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
677 }
678 
679 
680 /**
681   Check if the given table has any of the asked privileges
682 
683   @param thd		 Thread handler
684   @param want_access	 Bitmap of possible privileges to check for
685 
686   @retval
687     0  ok
688   @retval
689     1  error
690 */
691 
check_some_access(THD * thd,ulong want_access,TABLE_LIST * table)692 bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
693 {
694   ulong access;
695   DBUG_ENTER("check_some_access");
696 
697   /* This loop will work as long as we have less than 32 privileges */
698   for (access= 1; access < want_access ; access<<= 1)
699   {
700     if (access & want_access)
701     {
702       if (!check_access(thd, access, table->db,
703                         &table->grant.privilege,
704                         &table->grant.m_internal,
705                         0, 1) &&
706            !check_grant(thd, access, table, FALSE, 1, TRUE))
707         DBUG_RETURN(0);
708     }
709   }
710   DBUG_PRINT("exit",("no matching access rights"));
711   DBUG_RETURN(1);
712 }
713 
714 
715 /**
716   Check if the routine has any of the routine privileges.
717 
718   @param thd	       Thread handler
719   @param db           Database name
720   @param name         Routine name
721 
722   @retval
723     0            ok
724   @retval
725     1            error
726 */
727 
check_some_routine_access(THD * thd,const char * db,const char * name,bool is_proc)728 bool check_some_routine_access(THD *thd, const char *db, const char *name,
729                                bool is_proc)
730 {
731   ulong save_priv;
732   /*
733     The following test is just a shortcut for check_access() (to avoid
734     calculating db_access)
735     Note that this effectively bypasses the ACL_internal_schema_access checks
736     that are implemented for the INFORMATION_SCHEMA and PERFORMANCE_SCHEMA,
737     which are located in check_access().
738     Since the I_S and P_S do not contain routines, this bypass is ok,
739     as it only opens SHOW_PROC_ACLS.
740   */
741   if (thd->security_context()->check_access(SHOW_PROC_ACLS, true))
742     return FALSE;
743   if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, NULL, 0, 1) ||
744       (save_priv & SHOW_PROC_ACLS))
745     return FALSE;
746   return check_routine_level_acl(thd, db, name, is_proc);
747 }
748 
749 
750 
751 /**
752   @brief Compare requested privileges with the privileges acquired from the
753     User- and Db-tables.
754   @param thd          Thread handler
755   @param want_access  The requested access privileges.
756   @param db           A pointer to the Db name.
757   @param[out] save_priv A pointer to the granted privileges will be stored.
758   @param grant_internal_info A pointer to the internal grant cache.
759   @param dont_check_global_grants True if no global grants are checked.
760   @param no_error     True if no errors should be sent to the client.
761 
762   'save_priv' is used to save the User-table (global) and Db-table grants for
763   the supplied db name. Note that we don't store db level grants if the global
764   grants is enough to satisfy the request AND the global grants contains a
765   SELECT grant.
766 
767   For internal databases (INFORMATION_SCHEMA, PERFORMANCE_SCHEMA),
768   additional rules apply, see ACL_internal_schema_access.
769 
770   @see check_grant
771 
772   @return Status of denial of access by exclusive ACLs.
773     @retval FALSE Access can't exclusively be denied by Db- and User-table
774       access unless Column- and Table-grants are checked too.
775     @retval TRUE Access denied.
776 */
777 
778 bool
check_access(THD * thd,ulong want_access,const char * db,ulong * save_priv,GRANT_INTERNAL_INFO * grant_internal_info,bool dont_check_global_grants,bool no_errors)779 check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
780              GRANT_INTERNAL_INFO *grant_internal_info,
781              bool dont_check_global_grants, bool no_errors)
782 {
783   Security_context *sctx= thd->security_context();
784   ulong db_access;
785 
786   /*
787     GRANT command:
788     In case of database level grant the database name may be a pattern,
789     in case of table|column level grant the database name can not be a pattern.
790     We use 'dont_check_global_grants' as a flag to determine
791     if it's database level grant command
792     (see SQLCOM_GRANT case, mysql_execute_command() function) and
793     set db_is_pattern according to 'dont_check_global_grants' value.
794   */
795   bool  db_is_pattern= ((want_access & GRANT_ACL) && dont_check_global_grants);
796   ulong dummy;
797   DBUG_ENTER("check_access");
798   DBUG_PRINT("enter",("db: %s  want_access: %lu  master_access: %lu",
799                       db ? db : "", want_access, sctx->master_access()));
800 
801   if (save_priv)
802     *save_priv=0;
803   else
804   {
805     save_priv= &dummy;
806     dummy= 0;
807   }
808 
809   THD_STAGE_INFO(thd, stage_checking_permissions);
810   if ((!db || !db[0]) && !thd->db().str && !dont_check_global_grants)
811   {
812     DBUG_PRINT("error",("No database"));
813     if (!no_errors)
814       my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
815                  MYF(0));                       /* purecov: tested */
816     DBUG_RETURN(TRUE);				/* purecov: tested */
817   }
818 
819   if ((db != NULL) && (db != any_db))
820   {
821     const ACL_internal_schema_access *access;
822     access= get_cached_schema_access(grant_internal_info, db);
823     if (access)
824     {
825       switch (access->check(want_access, save_priv))
826       {
827       case ACL_INTERNAL_ACCESS_GRANTED:
828         /*
829           All the privileges requested have been granted internally.
830           [out] *save_privileges= Internal privileges.
831         */
832         DBUG_RETURN(FALSE);
833       case ACL_INTERNAL_ACCESS_DENIED:
834         if (! no_errors)
835         {
836           my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
837                    sctx->priv_user().str, sctx->priv_host().str, db);
838         }
839         DBUG_RETURN(TRUE);
840       case ACL_INTERNAL_ACCESS_CHECK_GRANT:
841         /*
842           Only some of the privilege requested have been granted internally,
843           proceed with the remaining bits of the request (want_access).
844         */
845         want_access&= ~(*save_priv);
846         break;
847       }
848     }
849   }
850 
851   if (sctx->check_access(want_access))
852   {
853     /*
854       1. If we don't have a global SELECT privilege, we have to get the
855       database specific access rights to be able to handle queries of type
856       UPDATE t1 SET a=1 WHERE b > 0
857       2. Change db access if it isn't current db which is being addressed
858     */
859     if (!(sctx->check_access(SELECT_ACL)))
860     {
861       if (db && (!thd->db().str || db_is_pattern ||
862                  strcmp(db, thd->db().str)))
863         db_access= acl_get(sctx->host().str, sctx->ip().str,
864                            sctx->priv_user().str, db, db_is_pattern);
865       else
866       {
867         /* get access for current db */
868         db_access= sctx->db_access();
869       }
870       /*
871         The effective privileges are the union of the global privileges
872         and the intersection of db- and host-privileges,
873         plus the internal privileges.
874       */
875       *save_priv|= sctx->master_access() | db_access;
876     }
877     else
878       *save_priv|= sctx->master_access();
879     DBUG_RETURN(FALSE);
880   }
881   if (((want_access & ~sctx->master_access()) & ~DB_ACLS) ||
882       (! db && dont_check_global_grants))
883   {						// We can never grant this
884     DBUG_PRINT("error",("No possible access"));
885     if (!no_errors)
886     {
887       if (thd->password == 2)
888         my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
889                  sctx->priv_user().str,
890                  sctx->priv_host().str);
891       else
892         my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
893                  sctx->priv_user().str,
894                  sctx->priv_host().str,
895                  (thd->password ?
896                   ER(ER_YES) :
897                   ER(ER_NO)));                    /* purecov: tested */
898     }
899     DBUG_RETURN(TRUE);				/* purecov: tested */
900   }
901 
902   if (db == any_db)
903   {
904     /*
905       Access granted; Allow select on *any* db.
906       [out] *save_privileges= 0
907     */
908     DBUG_RETURN(FALSE);
909   }
910 
911   if (db && (!thd->db().str || db_is_pattern || strcmp(db,thd->db().str)))
912     db_access= acl_get(sctx->host().str, sctx->ip().str,
913                        sctx->priv_user().str, db, db_is_pattern);
914   else
915     db_access= sctx->db_access();
916   DBUG_PRINT("info",("db_access: %lu  want_access: %lu",
917                      db_access, want_access));
918 
919   /*
920     Save the union of User-table and the intersection between Db-table and
921     Host-table privileges, with the already saved internal privileges.
922   */
923   db_access= (db_access | sctx->master_access());
924   *save_priv|= db_access;
925 
926   /*
927     We need to investigate column- and table access if all requested privileges
928     belongs to the bit set of .
929   */
930   bool need_table_or_column_check=
931     (want_access & (TABLE_ACLS | PROC_ACLS | db_access)) == want_access;
932 
933   /*
934     Grant access if the requested access is in the intersection of
935     host- and db-privileges (as retrieved from the acl cache),
936     also grant access if all the requested privileges are in the union of
937     TABLES_ACLS and PROC_ACLS; see check_grant.
938   */
939   if ( (db_access & want_access) == want_access ||
940       (!dont_check_global_grants &&
941        need_table_or_column_check))
942   {
943     /*
944        Ok; but need to check table- and column privileges.
945        [out] *save_privileges is (User-priv | (Db-priv & Host-priv) | Internal-priv)
946     */
947     DBUG_RETURN(FALSE);
948   }
949 
950   /*
951     Access is denied;
952     [out] *save_privileges is (User-priv | (Db-priv & Host-priv) | Internal-priv)
953   */
954   DBUG_PRINT("error",("Access denied"));
955   if (!no_errors)
956     my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
957              sctx->priv_user().str, sctx->priv_host().str,
958              (db ? db : (thd->db().str ?
959                          thd->db().str :
960                          "unknown")));
961   DBUG_RETURN(TRUE);
962 
963 }
964 
965 
966 /**
967   @brief Check if the requested privileges exists in either User-, Host- or
968     Db-tables.
969   @param thd          Thread context
970   @param want_access  Privileges requested
971   @param tables       List of tables to be compared against
972   @param no_errors    Don't report error to the client (using my_error() call).
973   @param any_combination_of_privileges_will_do TRUE if any privileges on any
974     column combination is enough.
975   @param number       Only the first 'number' tables in the linked list are
976                       relevant.
977 
978   The suppled table list contains cached privileges. This functions calls the
979   help functions check_access and check_grant to verify the first three steps
980   in the privileges check queue:
981   1. Global privileges
982   2. OR (db privileges AND host privileges)
983   3. OR table privileges
984   4. OR column privileges (not checked by this function!)
985   5. OR routine privileges (not checked by this function!)
986 
987   @see check_access
988   @see check_grant
989 
990   @note This functions assumes that table list used and
991   thd->lex->query_tables_own_last value correspond to each other
992   (the latter should be either 0 or point to next_global member
993   of one of elements of this table list).
994 
995   @return
996     @retval FALSE OK
997     @retval TRUE  Access denied; But column or routine privileges might need to
998       be checked also.
999 */
1000 
1001 bool
check_table_access(THD * thd,ulong requirements,TABLE_LIST * tables,bool any_combination_of_privileges_will_do,uint number,bool no_errors)1002 check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
1003 		   bool any_combination_of_privileges_will_do,
1004                    uint number, bool no_errors)
1005 {
1006   TABLE_LIST *org_tables= tables;
1007   TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
1008   uint i= 0;
1009   Security_context *sctx= thd->security_context();
1010   Security_context *backup_ctx= thd->security_context();
1011 
1012   DBUG_EXECUTE_IF("force_check_table_access_return_ok",
1013                   return false;);
1014   /*
1015     The check that first_not_own_table is not reached is for the case when
1016     the given table list refers to the list for prelocking (contains tables
1017     of other queries). For simple queries first_not_own_table is 0.
1018   */
1019   for (; i < number && tables != first_not_own_table && tables;
1020        tables= tables->next_global, i++)
1021   {
1022     TABLE_LIST *const table_ref= tables->correspondent_table ?
1023       tables->correspondent_table : tables;
1024     ulong want_access= requirements;
1025     if (table_ref->security_ctx)
1026       sctx= table_ref->security_ctx;
1027     else
1028       sctx= backup_ctx;
1029 
1030     /*
1031       We should not encounter table list elements for reformed SHOW
1032       statements unless this is first table list element in the main
1033       select.
1034       Such table list elements require additional privilege check
1035       (see check_show_access()). This check is carried out by caller,
1036       but only for the first table list element from the main select.
1037     */
1038     assert(!table_ref->schema_table_reformed ||
1039            table_ref == thd->lex->select_lex->table_list.first);
1040 
1041     DBUG_PRINT("info", ("derived: %d  view: %d", table_ref->is_derived(),
1042                         table_ref->is_view()));
1043 
1044     if (table_ref->is_derived())
1045       continue;
1046 
1047     thd->set_security_context(sctx);
1048 
1049     if (check_access(thd, want_access, table_ref->get_db_name(),
1050                      &table_ref->grant.privilege,
1051                      &table_ref->grant.m_internal,
1052                      0, no_errors))
1053       goto deny;
1054   }
1055   thd->set_security_context(backup_ctx);
1056   return check_grant(thd,requirements,org_tables,
1057                      any_combination_of_privileges_will_do,
1058                      number, no_errors);
1059 deny:
1060   thd->set_security_context(backup_ctx);
1061   return TRUE;
1062 }
1063 
1064 
1065 /****************************************************************************
1066   Handle GRANT commands
1067 ****************************************************************************/
1068 
1069 
1070 /*
1071   Return 1 if we are allowed to create new users
1072   the logic here is: INSERT_ACL is sufficient.
1073   It's also a requirement in opt_safe_user_create,
1074   otherwise CREATE_USER_ACL is enough.
1075 */
1076 
test_if_create_new_users(THD * thd)1077 static bool test_if_create_new_users(THD *thd)
1078 {
1079   Security_context *sctx= thd->security_context();
1080   bool create_new_users= MY_TEST(sctx->check_access(INSERT_ACL)) ||
1081                          (!opt_safe_user_create &&
1082                           MY_TEST(sctx->check_access(CREATE_USER_ACL)));
1083   if (!create_new_users)
1084   {
1085     TABLE_LIST tl;
1086     ulong db_access;
1087     tl.init_one_table(C_STRING_WITH_LEN("mysql"),
1088                       C_STRING_WITH_LEN("user"), "user", TL_WRITE);
1089     create_new_users= 1;
1090 
1091     db_access= acl_get(sctx->host().str, sctx->ip().str,
1092                        sctx->priv_user().str, tl.db, 0);
1093     if (!(db_access & INSERT_ACL))
1094     {
1095       if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
1096         create_new_users=0;
1097     }
1098   }
1099   return create_new_users;
1100 }
1101 
1102 
1103 /*
1104   Store table level and column level grants in the privilege tables
1105 
1106   SYNOPSIS
1107     mysql_table_grant()
1108     thd                 Thread handle
1109     table_list          List of tables to give grant
1110     user_list           List of users to give grant
1111     columns             List of columns to give grant
1112     rights              Table level grant
1113     revoke_grant        Set to 1 if this is a REVOKE command
1114 
1115   RETURN
1116     FALSE ok
1117     TRUE  error
1118 */
1119 
mysql_table_grant(THD * thd,TABLE_LIST * table_list,List<LEX_USER> & user_list,List<LEX_COLUMN> & columns,ulong rights,bool revoke_grant)1120 int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
1121                       List <LEX_USER> &user_list,
1122                       List <LEX_COLUMN> &columns, ulong rights,
1123                       bool revoke_grant)
1124 {
1125   ulong column_priv= 0;
1126   List_iterator <LEX_USER> str_list (user_list);
1127   LEX_USER *Str, *tmp_Str;
1128   TABLE_LIST tables[3];
1129   bool create_new_users=0;
1130   const char *db_name, *table_name;
1131   bool save_binlog_row_based;
1132   bool transactional_tables;
1133   ulong what_to_set= 0;
1134   bool is_privileged_user= false;
1135 
1136   DBUG_ENTER("mysql_table_grant");
1137 
1138   if (!initialized)
1139   {
1140     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
1141              "--skip-grant-tables");        /* purecov: inspected */
1142     DBUG_RETURN(TRUE);                      /* purecov: inspected */
1143   }
1144   if (rights & ~TABLE_ACLS)
1145   {
1146     my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
1147                MYF(0));
1148     DBUG_RETURN(TRUE);
1149   }
1150 
1151   if (!revoke_grant)
1152   {
1153     if (columns.elements)
1154     {
1155       class LEX_COLUMN *column;
1156       List_iterator <LEX_COLUMN> column_iter(columns);
1157 
1158       if (open_tables_for_query(thd, table_list, 0))
1159         DBUG_RETURN(TRUE);
1160 
1161       if (table_list->is_view())
1162       {
1163         if (table_list->resolve_derived(thd, false))
1164           DBUG_RETURN(true);             /* purecov: inspected */
1165 
1166         // Prepare a readonly (materialized) view for access to columns
1167         if (table_list->setup_materialized_derived(thd))
1168           DBUG_RETURN(true);             /* purecov: inspected */
1169       }
1170       while ((column = column_iter++))
1171       {
1172         uint unused_field_idx= NO_CACHED_FIELD_INDEX;
1173         TABLE_LIST *dummy;
1174         Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
1175                                          column->column.length(),
1176                                          column->column.ptr(), NULL, NULL,
1177                                          NULL,
1178                                          // check that we have the
1179                                          // to-be-granted privilege:
1180                                          column->rights,
1181                                          false,
1182                                          &unused_field_idx, false, &dummy);
1183         if (f == (Field*)0)
1184         {
1185           my_error(ER_BAD_FIELD_ERROR, MYF(0),
1186                    column->column.c_ptr(), table_list->alias);
1187           DBUG_RETURN(TRUE);
1188         }
1189         if (f == (Field *)-1)
1190           DBUG_RETURN(TRUE);
1191         column_priv|= column->rights;
1192       }
1193       close_mysql_tables(thd);
1194     }
1195     else
1196     {
1197       if (!(rights & CREATE_ACL))
1198       {
1199         char buf[FN_REFLEN + 1];
1200         build_table_filename(buf, sizeof(buf) - 1, table_list->db,
1201                              table_list->table_name, reg_ext, 0);
1202         fn_format(buf, buf, "", "", MY_UNPACK_FILENAME  | MY_RESOLVE_SYMLINKS |
1203                                     MY_RETURN_REAL_PATH | MY_APPEND_EXT);
1204         if (access(buf,F_OK))
1205         {
1206           my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
1207           DBUG_RETURN(TRUE);
1208         }
1209       }
1210       ulong missing_privilege= rights & ~table_list->grant.privilege;
1211       assert(missing_privilege == table_list->grant.want_privilege);
1212       if (missing_privilege)
1213       {
1214         char command[128];
1215         get_privilege_desc(command, sizeof(command), missing_privilege);
1216         my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
1217                  command, thd->security_context()->priv_user().str,
1218                  thd->security_context()->host_or_ip().str, table_list->alias);
1219         DBUG_RETURN(true);
1220       }
1221     }
1222   }
1223 
1224   /* open the mysql.tables_priv and mysql.columns_priv tables */
1225 
1226   tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
1227                            C_STRING_WITH_LEN("user"), "user", TL_WRITE);
1228   tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
1229                            C_STRING_WITH_LEN("tables_priv"),
1230                            "tables_priv", TL_WRITE);
1231   tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
1232                            C_STRING_WITH_LEN("columns_priv"),
1233                            "columns_priv", TL_WRITE);
1234   tables[0].next_local= tables[0].next_global= tables+1;
1235   /* Don't open column table if we don't need it ! */
1236   if (column_priv || (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
1237     tables[1].next_local= tables[1].next_global= tables+2;
1238 
1239   /*
1240     This statement will be replicated as a statement, even when using
1241     row-based replication.  The flag will be reset at the end of the
1242     statement.
1243   */
1244   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
1245     thd->clear_current_stmt_binlog_format_row();
1246 
1247 #ifdef HAVE_REPLICATION
1248   /*
1249     GRANT and REVOKE are applied the slave in/exclusion rules as they are
1250     some kind of updates to the mysql.% tables.
1251   */
1252   if (thd->slave_thread && rpl_filter->is_on())
1253   {
1254     /*
1255       The tables must be marked "updating" so that tables_ok() takes them into
1256       account in tests.
1257     */
1258     tables[0].updating= tables[1].updating= tables[2].updating= 1;
1259     if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, tables)))
1260     {
1261       /* Restore the state of binlog format */
1262       assert(!thd->is_current_stmt_binlog_format_row());
1263       if (save_binlog_row_based)
1264         thd->set_current_stmt_binlog_format_row();
1265       DBUG_RETURN(FALSE);
1266     }
1267   }
1268 #endif /* HAVE_REPLICATION */
1269 
1270   /*
1271     The lock api is depending on the thd->lex variable which needs to be
1272     re-initialized.
1273   */
1274   Query_tables_list backup;
1275   thd->lex->reset_n_backup_query_tables_list(&backup);
1276   /*
1277     Restore Query_tables_list::sql_command value, which was reset
1278     above, as the code writing query to the binary log assumes that
1279     this value corresponds to the statement being executed.
1280   */
1281   thd->lex->sql_command= backup.sql_command;
1282   if (open_and_lock_tables(thd, tables, MYSQL_LOCK_IGNORE_TIMEOUT))
1283   {                                             // Should never happen
1284     /* Restore the state of binlog format */
1285     assert(!thd->is_current_stmt_binlog_format_row());
1286     thd->lex->restore_backup_query_tables_list(&backup);
1287     if (save_binlog_row_based)
1288       thd->set_current_stmt_binlog_format_row();
1289     DBUG_RETURN(TRUE);                          /* purecov: deadcode */
1290   }
1291 
1292   transactional_tables= (tables[0].table->file->has_transactions() ||
1293                          tables[1].table->file->has_transactions() ||
1294                          (tables[2].table &&
1295                           tables[2].table->file->has_transactions()));
1296 
1297   if (!revoke_grant)
1298     create_new_users= test_if_create_new_users(thd);
1299   bool result= FALSE;
1300   bool is_partial_execution= false;
1301 
1302   is_privileged_user= is_privileged_user_for_credential_change(thd);
1303 
1304   Partitioned_rwlock_write_guard lock(&LOCK_grant);
1305   mysql_mutex_lock(&acl_cache->lock);
1306   MEM_ROOT *old_root= thd->mem_root;
1307   thd->mem_root= &memex;
1308   grant_version++;
1309 
1310   bool rollback_whole_statement= false;
1311   while ((tmp_Str = str_list++))
1312   {
1313     int error;
1314     GRANT_TABLE *grant_table;
1315 
1316     if (!(Str= get_current_user(thd, tmp_Str)))
1317     {
1318       result= TRUE;
1319       continue;
1320     }
1321 
1322     if (set_and_validate_user_attributes(thd, Str, what_to_set,
1323                                          is_privileged_user,
1324                                          revoke_grant?"REVOKE":"GRANT"))
1325     {
1326       result= TRUE;
1327       continue;
1328     }
1329 
1330     ACL_USER *acl_user= find_acl_user(Str->host.str, Str->user.str, TRUE);
1331 
1332     /* Create user if needed */
1333     error= replace_user_table(thd, tables[0].table, Str,
1334                               0, revoke_grant, create_new_users,
1335                               what_to_set);
1336     /*
1337       If the user did not exist and replace_user_table() succeeded and if this
1338       is a GRANT statement, then it means that a new user is created.
1339 
1340       So, set the is_partial_execution flag to true.
1341     */
1342     if (!error)
1343       is_partial_execution= (!acl_user && !revoke_grant) || is_partial_execution;
1344 
1345     if (error > 0)
1346     {
1347       result= TRUE;                             // Remember error
1348       continue;                                 // Add next user
1349     }
1350     else if (error < 0)
1351     {
1352       rollback_whole_statement= true;
1353       result= true;
1354       break;
1355     }
1356     db_name= table_list->get_db_name();
1357     thd->add_to_binlog_accessed_dbs(db_name); // collecting db:s for MTS
1358     table_name= table_list->get_table_name();
1359 
1360     /* Find/create cached table grant */
1361     grant_table= table_hash_search(Str->host.str, NullS, db_name,
1362                                    Str->user.str, table_name, 1);
1363     if (!grant_table)
1364     {
1365       if (revoke_grant)
1366       {
1367         my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
1368                  Str->user.str, Str->host.str, table_list->table_name);
1369         result= TRUE;
1370         continue;
1371       }
1372       grant_table = new GRANT_TABLE (Str->host.str, db_name,
1373                                      Str->user.str, table_name,
1374                                      rights,
1375                                      column_priv);
1376       if (!grant_table ||
1377         my_hash_insert(&column_priv_hash,(uchar*) grant_table))
1378       {
1379         rollback_whole_statement= true;
1380         result= TRUE;                           /* purecov: deadcode */
1381         break;                               /* purecov: deadcode */
1382       }
1383     }
1384 
1385     /* If revoke_grant, calculate the new column privilege for tables_priv */
1386     if (revoke_grant)
1387     {
1388       class LEX_COLUMN *column;
1389       List_iterator <LEX_COLUMN> column_iter(columns);
1390       GRANT_COLUMN *grant_column;
1391 
1392       /* Fix old grants */
1393       while ((column = column_iter++))
1394       {
1395         grant_column = column_hash_search(grant_table,
1396                                           column->column.ptr(),
1397                                           column->column.length());
1398         if (grant_column)
1399           grant_column->rights&= ~(column->rights | rights);
1400       }
1401       /* scan trough all columns to get new column grant */
1402       column_priv= 0;
1403       for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
1404       {
1405         grant_column= (GRANT_COLUMN*)
1406           my_hash_element(&grant_table->hash_columns, idx);
1407         grant_column->rights&= ~rights;         // Fix other columns
1408         column_priv|= grant_column->rights;
1409       }
1410     }
1411     else
1412     {
1413       column_priv|= grant_table->cols;
1414     }
1415 
1416     /* update table and columns */
1417 
1418     error= replace_table_table(thd, grant_table, tables[1].table, *Str,
1419                                db_name, table_name,
1420                                rights, column_priv, revoke_grant);
1421 
1422     if (error > 0)
1423     {
1424       result= true;
1425       continue;
1426     }
1427     else if (error < 0)
1428     {
1429       rollback_whole_statement= true;
1430       result= true;
1431       break;
1432     }
1433 
1434     if (tables[2].table)
1435     {
1436       error= replace_column_table(grant_table, tables[2].table, *Str,
1437                                   columns,
1438                                   db_name, table_name,
1439                                   rights, revoke_grant);
1440       if (error > 0)
1441       {
1442         result= true;
1443         continue;
1444       }
1445       else if (error < 0)
1446       {
1447         rollback_whole_statement= true;
1448         result= true;
1449         break;
1450       }
1451     }
1452     is_partial_execution= true;
1453   }
1454   thd->mem_root= old_root;
1455   mysql_mutex_unlock(&acl_cache->lock);
1456 
1457   /*
1458     We only log "complete" successful commands, because partially
1459     failed REVOKE/GRANTS that fail because of insufficient privileges
1460     on the master, will succeed on the slave due to SQL thread SUPER
1461     privilege. Even though replication will stop (the error code from
1462     the master will mismatch the error code on the slave), the
1463     operation will already be executed (thence revoking or granting
1464     additional privileges on the slave).
1465     Before ACLs are changed to execute fully or none at all, when
1466     some error happens, write an incident if one or more users are
1467     granted/revoked successfully (it has a partial execution).
1468   */
1469   if (result)
1470   {
1471     if (!rollback_whole_statement || !transactional_tables)
1472     {
1473       if (is_partial_execution)
1474       {
1475         const char* err_msg= "REVOKE/GRANT failed while storing table level "
1476                              "and column level grants in the privilege tables.";
1477         mysql_bin_log.write_incident(thd, true /* need_lock_log=true */,
1478                                      err_msg);
1479       }
1480     }
1481   }
1482   else
1483   {
1484     /*
1485       Rewrite (table) GRANT statements to use password hashes
1486       instead of <secret> style obfuscation so it can be used
1487       in binlog.
1488     */
1489     if (!revoke_grant) {
1490       String rlb;
1491       mysql_rewrite_grant(thd, &rlb);
1492       thd->swap_rewritten_query(rlb);
1493     }
1494 
1495     if (thd->rewritten_query().length() > 0)
1496       result= result |
1497           write_bin_log(thd, FALSE, thd->rewritten_query().ptr(),
1498                         thd->rewritten_query().length(),
1499                         transactional_tables);
1500     else
1501       result= result |
1502         write_bin_log(thd, FALSE, thd->query().str, thd->query().length,
1503                             transactional_tables);
1504   }
1505 
1506   lock.unlock();
1507 
1508   result|=
1509     acl_end_trans_and_close_tables(thd,
1510                                    thd->transaction_rollback_request ||
1511                                    rollback_whole_statement);
1512 
1513   if (!result) /* success */
1514   {
1515     acl_notify_htons(thd, thd->query().str, thd->query().length);
1516     my_ok(thd);
1517   }
1518 
1519   thd->lex->restore_backup_query_tables_list(&backup);
1520   /* Restore the state of binlog format */
1521   assert(!thd->is_current_stmt_binlog_format_row());
1522   if (save_binlog_row_based)
1523     thd->set_current_stmt_binlog_format_row();
1524   DBUG_RETURN(result);
1525 }
1526 
1527 
1528 /**
1529   Store routine level grants in the privilege tables
1530 
1531   @param thd Thread handle
1532   @param table_list List of routines to give grant
1533   @param is_proc Is this a list of procedures?
1534   @param user_list List of users to give grant
1535   @param rights Table level grant
1536   @param revoke_grant Is this is a REVOKE command?
1537 
1538   @return
1539     @retval FALSE Success.
1540     @retval TRUE An error occurred.
1541 */
1542 
mysql_routine_grant(THD * thd,TABLE_LIST * table_list,bool is_proc,List<LEX_USER> & user_list,ulong rights,bool revoke_grant,bool write_to_binlog)1543 bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
1544                          List <LEX_USER> &user_list, ulong rights,
1545                          bool revoke_grant, bool write_to_binlog)
1546 {
1547   List_iterator <LEX_USER> str_list (user_list);
1548   LEX_USER *Str, *tmp_Str;
1549   TABLE_LIST tables[2];
1550   bool create_new_users=0, result=0;
1551   const char *db_name, *table_name;
1552   bool save_binlog_row_based;
1553   bool transactional_tables;
1554   ulong what_to_set= 0;
1555   bool is_privileged_user= false;
1556 
1557   DBUG_ENTER("mysql_routine_grant");
1558 
1559   if (!initialized)
1560   {
1561     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
1562              "--skip-grant-tables");
1563     DBUG_RETURN(TRUE);
1564   }
1565   if (rights & ~PROC_ACLS)
1566   {
1567     my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
1568                MYF(0));
1569     DBUG_RETURN(TRUE);
1570   }
1571 
1572   if (!revoke_grant)
1573   {
1574     if (sp_exist_routines(thd, table_list, is_proc))
1575       DBUG_RETURN(TRUE);
1576   }
1577 
1578   /* open the mysql.user and mysql.procs_priv tables */
1579 
1580   tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
1581                            C_STRING_WITH_LEN("user"), "user", TL_WRITE);
1582   tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
1583                            C_STRING_WITH_LEN("procs_priv"), "procs_priv", TL_WRITE);
1584   tables[0].next_local= tables[0].next_global= tables+1;
1585 
1586   /*
1587     This statement will be replicated as a statement, even when using
1588     row-based replication.  The flag will be reset at the end of the
1589     statement.
1590   */
1591   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
1592     thd->clear_current_stmt_binlog_format_row();
1593 
1594 #ifdef HAVE_REPLICATION
1595   /*
1596     GRANT and REVOKE are applied the slave in/exclusion rules as they are
1597     some kind of updates to the mysql.% tables.
1598   */
1599   if (thd->slave_thread && rpl_filter->is_on())
1600   {
1601     /*
1602       The tables must be marked "updating" so that tables_ok() takes them into
1603       account in tests.
1604     */
1605     tables[0].updating= tables[1].updating= 1;
1606     if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, tables)))
1607     {
1608       /* Restore the state of binlog format */
1609       assert(!thd->is_current_stmt_binlog_format_row());
1610       if (save_binlog_row_based)
1611         thd->set_current_stmt_binlog_format_row();
1612       DBUG_RETURN(FALSE);
1613     }
1614   }
1615 #endif /* HAVE_REPLICATION */
1616 
1617   if (open_and_lock_tables(thd, tables, MYSQL_LOCK_IGNORE_TIMEOUT))
1618   {                                             // Should never happen
1619     /* Restore the state of binlog format */
1620     assert(!thd->is_current_stmt_binlog_format_row());
1621     if (save_binlog_row_based)
1622       thd->set_current_stmt_binlog_format_row();
1623     DBUG_RETURN(TRUE);
1624   }
1625 
1626   transactional_tables= (tables[0].table->file->has_transactions() ||
1627                          tables[1].table->file->has_transactions());
1628 
1629   if (!revoke_grant)
1630     create_new_users= test_if_create_new_users(thd);
1631 
1632   is_privileged_user= is_privileged_user_for_credential_change(thd);
1633   Partitioned_rwlock_write_guard lock(&LOCK_grant);
1634   mysql_mutex_lock(&acl_cache->lock);
1635   MEM_ROOT *old_root= thd->mem_root;
1636   thd->mem_root= &memex;
1637 
1638   DBUG_PRINT("info",("now time to iterate and add users"));
1639 
1640   bool is_partial_execution= false;
1641   bool rollback_whole_statement= false;
1642   while ((tmp_Str= str_list++))
1643   {
1644     int error;
1645     GRANT_NAME *grant_name;
1646 
1647     if (!(Str= get_current_user(thd, tmp_Str)))
1648     {
1649       result= TRUE;
1650       continue;
1651     }
1652 
1653     if (set_and_validate_user_attributes(thd, Str, what_to_set,
1654                                          is_privileged_user,
1655                                          revoke_grant?"REVOKE":"GRANT"))
1656     {
1657       result= TRUE;
1658       continue;
1659     }
1660 
1661     ACL_USER *acl_user= find_acl_user(Str->host.str, Str->user.str, TRUE);
1662 
1663     /* Create user if needed */
1664     error= replace_user_table(thd, tables[0].table, Str,
1665                               0, revoke_grant, create_new_users,
1666                               what_to_set);
1667     /*
1668       If the user did not exist and replace_user_table() succeeded and if this
1669       is a GRANT statement, then it means that a new user is created.
1670 
1671       So, set the is_partial_execution flag to true.
1672     */
1673     if (!error)
1674       is_partial_execution= (!acl_user && !revoke_grant) || is_partial_execution;
1675 
1676     if (error > 0)
1677     {
1678       result= TRUE;                             // Remember error
1679       continue;                                 // Add next user
1680     }
1681     else if (error < 0)
1682     {
1683       rollback_whole_statement= true;
1684       result= true;
1685       break;
1686     }
1687     db_name= table_list->db;
1688     if (write_to_binlog)
1689       thd->add_to_binlog_accessed_dbs(db_name);
1690     table_name= table_list->table_name;
1691     grant_name= routine_hash_search(Str->host.str, NullS, db_name,
1692                                     Str->user.str, table_name, is_proc, 1);
1693     if (!grant_name)
1694     {
1695       if (revoke_grant)
1696       {
1697         my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
1698                  Str->user.str, Str->host.str, table_name);
1699         result= TRUE;
1700         continue;
1701       }
1702       grant_name= new GRANT_NAME(Str->host.str, db_name,
1703                                  Str->user.str, table_name,
1704                                  rights, TRUE);
1705       if (!grant_name ||
1706         my_hash_insert(is_proc ?
1707                        &proc_priv_hash : &func_priv_hash,(uchar*) grant_name))
1708       {
1709         result= TRUE;
1710         rollback_whole_statement= true;
1711         break;
1712       }
1713     }
1714 
1715     error= replace_routine_table(thd, grant_name, tables[1].table, *Str,
1716                                  db_name, table_name, is_proc, rights,
1717                                  revoke_grant);
1718     if (error > 0)
1719     {
1720       result= TRUE;
1721       continue;
1722     }
1723     else if (error < 0)
1724     {
1725       result= true;
1726       rollback_whole_statement= true;
1727       break;
1728     }
1729     is_partial_execution= true;
1730   }
1731   thd->mem_root= old_root;
1732   mysql_mutex_unlock(&acl_cache->lock);
1733 
1734   if (write_to_binlog)
1735   {
1736     /*
1737       Before ACLs are changed to execute fully or none at all, when
1738       some error happens, write an incident if one or more users are
1739       granted/revoked successfully (it has a partial execution).
1740     */
1741     if (result)
1742     {
1743       if (!rollback_whole_statement || !transactional_tables)
1744       {
1745         if (is_partial_execution)
1746         {
1747           const char* err_msg= "REVOKE/GRANT failed while storing routine "
1748                                "level grants in the privilege tables.";
1749           mysql_bin_log.write_incident(thd, true /* need_lock_log=true */,
1750                                        err_msg);
1751         }
1752       }
1753     }
1754     else
1755     {
1756       /*
1757         Rewrite (routine) GRANT statements to use password hashes
1758         instead of <secret> style obfuscation so it can be used
1759         in binlog.
1760       */
1761       if (!revoke_grant) {
1762         String rlb;
1763         mysql_rewrite_grant(thd, &rlb);
1764         thd->swap_rewritten_query(rlb);
1765       }
1766 
1767       /*
1768         For performance reasons, we don't rewrite the query if we don't have to.
1769         If that was the case, write the original query.
1770       */
1771       if (thd->rewritten_query().length() == 0)
1772       {
1773         if (write_bin_log(thd, false, thd->query().str, thd->query().length,
1774                           transactional_tables))
1775           result= TRUE;
1776       }
1777       else
1778       {
1779         if (write_bin_log(thd, false, thd->rewritten_query().ptr(),
1780                           thd->rewritten_query().length(),
1781                           transactional_tables))
1782           result= TRUE;
1783       }
1784     }
1785   }
1786 
1787   lock.unlock();
1788 
1789   result|=
1790     acl_end_trans_and_close_tables(thd,
1791                                    thd->transaction_rollback_request ||
1792                                    rollback_whole_statement);
1793 
1794   if (write_to_binlog && !result)
1795     acl_notify_htons(thd, thd->query().str, thd->query().length);
1796 
1797   /* Restore the state of binlog format */
1798   assert(!thd->is_current_stmt_binlog_format_row());
1799   if (save_binlog_row_based)
1800     thd->set_current_stmt_binlog_format_row();
1801 
1802   DBUG_RETURN(result);
1803 }
1804 
1805 
mysql_grant(THD * thd,const char * db,List<LEX_USER> & list,ulong rights,bool revoke_grant,bool is_proxy)1806 bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
1807                  ulong rights, bool revoke_grant, bool is_proxy)
1808 {
1809   List_iterator <LEX_USER> str_list (list);
1810   LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
1811   char tmp_db[NAME_LEN+1];
1812   bool create_new_users=0;
1813   TABLE_LIST tables[2];
1814   bool save_binlog_row_based;
1815   bool transactional_tables;
1816   ulong what_to_set= 0;
1817   bool is_privileged_user= false;
1818 
1819   DBUG_ENTER("mysql_grant");
1820   if (!initialized)
1821   {
1822     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
1823              "--skip-grant-tables");    /* purecov: tested */
1824     DBUG_RETURN(TRUE);                  /* purecov: tested */
1825   }
1826 
1827   if (lower_case_table_names && db)
1828   {
1829     my_stpnmov(tmp_db,db,NAME_LEN);
1830     tmp_db[NAME_LEN]= '\0';
1831     my_casedn_str(files_charset_info, tmp_db);
1832     db=tmp_db;
1833   }
1834 
1835   if (is_proxy)
1836   {
1837     assert(!db);
1838     proxied_user= str_list++;
1839   }
1840 
1841   /* open the mysql.user and mysql.db or mysql.proxies_priv tables */
1842   tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
1843                            C_STRING_WITH_LEN("user"), "user", TL_WRITE);
1844   if (is_proxy)
1845 
1846     tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
1847                              C_STRING_WITH_LEN("proxies_priv"),
1848                              "proxies_priv",
1849                              TL_WRITE);
1850   else
1851     tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
1852                              C_STRING_WITH_LEN("db"),
1853                              "db",
1854                              TL_WRITE);
1855   tables[0].next_local= tables[0].next_global= tables+1;
1856 
1857   /*
1858     This statement will be replicated as a statement, even when using
1859     row-based replication.  The flag will be reset at the end of the
1860     statement.
1861   */
1862   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
1863     thd->clear_current_stmt_binlog_format_row();
1864 
1865 #ifdef HAVE_REPLICATION
1866   /*
1867     GRANT and REVOKE are applied the slave in/exclusion rules as they are
1868     some kind of updates to the mysql.% tables.
1869   */
1870   if (thd->slave_thread && rpl_filter->is_on())
1871   {
1872     /*
1873       The tables must be marked "updating" so that tables_ok() takes them into
1874       account in tests.
1875     */
1876     tables[0].updating= tables[1].updating= 1;
1877     if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, tables)))
1878     {
1879       /* Restore the state of binlog format */
1880       assert(!thd->is_current_stmt_binlog_format_row());
1881       if (save_binlog_row_based)
1882         thd->set_current_stmt_binlog_format_row();
1883       DBUG_RETURN(FALSE);
1884     }
1885   }
1886 #endif /*HAVE_REPLICATION */
1887 
1888   if (open_and_lock_tables(thd, tables, MYSQL_LOCK_IGNORE_TIMEOUT))
1889   {                                     // This should never happen
1890     /* Restore the state of binlog format */
1891     assert(!thd->is_current_stmt_binlog_format_row());
1892     if (save_binlog_row_based)
1893       thd->set_current_stmt_binlog_format_row();
1894     DBUG_RETURN(TRUE);                  /* purecov: deadcode */
1895   }
1896 
1897   transactional_tables= (tables[0].table->file->has_transactions() ||
1898                          tables[1].table->file->has_transactions());
1899 
1900   if (!revoke_grant)
1901     create_new_users= test_if_create_new_users(thd);
1902 
1903   is_privileged_user= is_privileged_user_for_credential_change(thd);
1904   /* go through users in user_list */
1905   Partitioned_rwlock_write_guard lock(&LOCK_grant);
1906   mysql_mutex_lock(&acl_cache->lock);
1907   grant_version++;
1908 
1909   int result= 0;
1910   bool is_partial_execution= false;
1911   bool rollback_whole_statement= false;
1912   while ((tmp_Str = str_list++))
1913   {
1914     if (!(Str= get_current_user(thd, tmp_Str)))
1915     {
1916       result= TRUE;
1917       continue;
1918     }
1919 
1920     if (set_and_validate_user_attributes(thd, Str, what_to_set,
1921                                          is_privileged_user,
1922                                          revoke_grant?"REVOKE":"GRANT"))
1923     {
1924       result= TRUE;
1925       continue;
1926     }
1927 
1928     ACL_USER *acl_user= find_acl_user(Str->host.str, Str->user.str, TRUE);
1929     int ret= replace_user_table(thd, tables[0].table, Str,
1930                                 (!db ? rights : 0), revoke_grant,
1931                                 create_new_users,
1932                                 (what_to_set | ACCESS_RIGHTS_ATTR));
1933     /*
1934       If the user did not exist and replace_user_table() succeeded and if
1935       this is a GRANT statement, then it means that a new user is created.
1936       So, set the is_partial_execution flag to true.
1937     */
1938     if (!ret)
1939     {
1940       /* In case of GRANT, user creation is partial execution */
1941       is_partial_execution= (!acl_user && !revoke_grant) || is_partial_execution;
1942     }
1943 
1944     if (ret)
1945     {
1946       result= -1;
1947       if (ret < 0)
1948       {
1949         /*
1950           If error in storage egine or system error happen then
1951           it doesn't make sense to continue handling of statement's
1952           arguments (users list). In this case leave a loop, rollback
1953           the whole statement and return an error.
1954         */
1955         rollback_whole_statement= true;
1956         break;
1957       }
1958       continue;
1959     }
1960     else if (db)
1961     {
1962       ulong db_rights= rights & DB_ACLS;
1963       if (db_rights  == rights)
1964       {
1965         ret= replace_db_table(tables[1].table, db, *Str, db_rights,
1966                               revoke_grant);
1967         if (ret)
1968         {
1969           result= -1;
1970           if (ret < 0)
1971           {
1972             /*
1973               If error in storage egine or system error happen then
1974               it doesn't make sense to continue handling of statement's
1975               arguments (users list). In this case leave a loop, rollback
1976               the whole statement and return an error.
1977             */
1978             rollback_whole_statement= true;
1979             break;
1980           }
1981           continue;
1982         }
1983         thd->add_to_binlog_accessed_dbs(db);
1984       }
1985       else
1986       {
1987         my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
1988         result= -1;
1989         continue;
1990       }
1991     }
1992     else if (is_proxy)
1993     {
1994       ret= replace_proxies_priv_table(thd, tables[1].table, Str, proxied_user,
1995                                       rights & GRANT_ACL ? true : false,
1996                                       revoke_grant);
1997       if (ret)
1998       {
1999         result= -1;
2000         if (ret < 0)
2001         {
2002           /*
2003             If error in storage egine or system error happen then
2004             it doesn't make sense to continue handling of statement's
2005             arguments (users list). In this case leave a loop, rollback
2006             the whole statement and return an error.
2007           */
2008           rollback_whole_statement= true;
2009           break;
2010         }
2011         continue;
2012       }
2013     }
2014     is_partial_execution= true;
2015   }
2016   mysql_mutex_unlock(&acl_cache->lock);
2017 
2018   /*
2019     Before ACLs are changed to execute fully or none at all, when
2020     some error happens, write an incident if one or more users are
2021     granted/revoked successfully (it has a partial execution).
2022   */
2023   if (result)
2024   {
2025     if (!rollback_whole_statement || !transactional_tables)
2026     {
2027       if (is_partial_execution)
2028       {
2029         const char* err_msg= "REVOKE/GRANT failed while granting/revoking "
2030                              "privileges in databases.";
2031         mysql_bin_log.write_incident(thd, true /* need_lock_log=true */,
2032                                      err_msg);
2033       }
2034     }
2035   }
2036   else
2037   {
2038     /*
2039       Rewrite GRANT statements to use password hashes instead of
2040       <secret> style obfuscation so it can be used in binlog.
2041     */
2042     if (!revoke_grant) {
2043       String rlb;
2044       mysql_rewrite_grant(thd, &rlb);
2045       thd->swap_rewritten_query(rlb);
2046     }
2047 
2048     if (thd->rewritten_query().length() > 0) {
2049       result= result |
2050           write_bin_log(thd, FALSE, thd->rewritten_query().ptr(),
2051                         thd->rewritten_query().length(),
2052                         transactional_tables);
2053     }
2054     else
2055       result= result |
2056         write_bin_log(thd, FALSE, thd->query().str, thd->query().length,
2057                             transactional_tables);
2058   }
2059 
2060   lock.unlock();
2061 
2062   result|=
2063     acl_end_trans_and_close_tables(thd,
2064                                    thd->transaction_rollback_request ||
2065                                    rollback_whole_statement);
2066 
2067   if (!result)
2068   {
2069     acl_notify_htons(thd, thd->query().str, thd->query().length);
2070     my_ok(thd);
2071   }
2072 
2073   /* Restore the state of binlog format */
2074   assert(!thd->is_current_stmt_binlog_format_row());
2075   if (save_binlog_row_based)
2076     thd->set_current_stmt_binlog_format_row();
2077 
2078   DBUG_RETURN(result);
2079 }
2080 
2081 
2082 
2083 
2084 /**
2085   @brief Check table level grants
2086 
2087   @param thd          Thread handler
2088   @param want_access  Bits of privileges user needs to have.
2089   @param tables       List of tables to check. The user should have
2090                       'want_access' to all tables in list.
2091   @param any_combination_will_do TRUE if it's enough to have any privilege for
2092     any combination of the table columns.
2093   @param number       Check at most this number of tables.
2094   @param no_errors    TRUE if no error should be sent directly to the client.
2095 
2096   If table->grant.want_privilege != 0 then the requested privileges where
2097   in the set of COL_ACLS but access was not granted on the table level. As
2098   a consequence an extra check of column privileges is required.
2099 
2100   Specifically if this function returns FALSE the user has some kind of
2101   privilege on a combination of columns in each table.
2102 
2103   This function is usually preceeded by check_access which establish the
2104   User-, Db- and Host access rights.
2105 
2106   @see check_access
2107   @see check_table_access
2108 
2109   @note This functions assumes that either number of tables to be inspected
2110      by it is limited explicitly (i.e. is is not UINT_MAX) or table list
2111      used and thd->lex->query_tables_own_last value correspond to each
2112      other (the latter should be either 0 or point to next_global member
2113      of one of elements of this table list).
2114 
2115    @return Access status
2116      @retval FALSE Access granted; But column privileges might need to be
2117       checked.
2118      @retval TRUE The user did not have the requested privileges on any of the
2119       tables.
2120 
2121 */
2122 
check_grant(THD * thd,ulong want_access,TABLE_LIST * tables,bool any_combination_will_do,uint number,bool no_errors)2123 bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
2124                  bool any_combination_will_do, uint number, bool no_errors)
2125 {
2126   TABLE_LIST *tl;
2127   TABLE_LIST *const first_not_own_table= thd->lex->first_not_own_table();
2128   Security_context *sctx= thd->security_context();
2129   ulong orig_want_access= want_access;
2130   DBUG_ENTER("check_grant");
2131   assert(number > 0);
2132 
2133   LOCK_grant_read_guard lock(thd);
2134   for (tl= tables;
2135        tl && number-- && tl != first_not_own_table;
2136        tl= tl->next_global)
2137   {
2138     TABLE_LIST *const t_ref=
2139       tl->correspondent_table ? tl->correspondent_table : tl;
2140     sctx = MY_TEST(t_ref->security_ctx) ? t_ref->security_ctx :
2141                                           thd->security_context();
2142 
2143     const ACL_internal_table_access *access=
2144       get_cached_table_access(&t_ref->grant.m_internal,
2145                               t_ref->get_db_name(),
2146                               t_ref->get_table_name());
2147 
2148     if (access)
2149     {
2150       switch(access->check(orig_want_access, &t_ref->grant.privilege))
2151       {
2152       case ACL_INTERNAL_ACCESS_GRANTED:
2153         /*
2154            Grant all access to the table to skip column checks.
2155            Depend on the controls in the P_S table itself.
2156         */
2157         t_ref->grant.privilege|= TMP_TABLE_ACLS;
2158 #ifndef NDEBUG
2159         t_ref->grant.want_privilege= 0;
2160 #endif
2161         continue;
2162       case ACL_INTERNAL_ACCESS_DENIED:
2163         goto err;
2164       case ACL_INTERNAL_ACCESS_CHECK_GRANT:
2165         break;
2166       }
2167     }
2168 
2169     want_access= orig_want_access;
2170     want_access&= ~sctx->master_access();
2171     if (!want_access)
2172       continue;                                 // ok
2173 
2174     if (!(~t_ref->grant.privilege & want_access) ||
2175         t_ref->is_derived() || t_ref->schema_table)
2176     {
2177       /*
2178         It is subquery in the FROM clause. VIEW set t_ref->derived after
2179         table opening, but this function always called before table opening.
2180       */
2181       if (!t_ref->referencing_view)
2182       {
2183         /*
2184           If it's a temporary table created for a subquery in the FROM
2185           clause, or an INFORMATION_SCHEMA table, drop the request for
2186           a privilege.
2187         */
2188 #ifndef NDEBUG
2189         t_ref->grant.want_privilege= 0;
2190 #endif
2191       }
2192       continue;
2193     }
2194 
2195     if (is_temporary_table(t_ref))
2196     {
2197       /*
2198         If this table list element corresponds to a pre-opened temporary
2199         table skip checking of all relevant table-level privileges for it.
2200         Note that during creation of temporary table we still need to check
2201         if user has CREATE_TMP_ACL.
2202       */
2203       t_ref->grant.privilege|= TMP_TABLE_ACLS;
2204 #ifndef NDEBUG
2205       t_ref->grant.want_privilege= 0;
2206 #endif
2207       continue;
2208     }
2209 
2210     GRANT_TABLE *grant_table= table_hash_search(sctx->host().str,
2211                                                 sctx->ip().str,
2212                                                 t_ref->get_db_name(),
2213                                                 sctx->priv_user().str,
2214                                                 t_ref->get_table_name(),
2215                                                 FALSE);
2216 
2217     if (!grant_table)
2218     {
2219       want_access &= ~t_ref->grant.privilege;
2220       goto err;                                 // No grants
2221     }
2222 
2223     /*
2224       For SHOW COLUMNS, SHOW INDEX it is enough to have some
2225       privileges on any column combination on the table.
2226     */
2227     if (any_combination_will_do)
2228       continue;
2229 
2230     t_ref->grant.grant_table= grant_table; // Remember for column test
2231     t_ref->grant.version= grant_version;
2232     t_ref->grant.privilege|= grant_table->privs;
2233     t_ref->set_want_privilege(want_access & COL_ACLS);
2234 
2235     if (!(~t_ref->grant.privilege & want_access))
2236       continue;
2237 
2238     if (want_access & ~(grant_table->cols | t_ref->grant.privilege))
2239     {
2240       want_access &= ~(grant_table->cols | t_ref->grant.privilege);
2241       goto err;                                 // impossible
2242     }
2243   }
2244   DBUG_RETURN(FALSE);
2245 
2246 err:
2247   lock.unlock();
2248   if (!no_errors)                               // Not a silent skip of table
2249   {
2250     char command[128];
2251     get_privilege_desc(command, sizeof(command), want_access);
2252     my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
2253              command,
2254              sctx->priv_user().str,
2255              sctx->host_or_ip().str,
2256              tl ? tl->get_table_name() : "unknown");
2257   }
2258   DBUG_RETURN(TRUE);
2259 }
2260 
2261 
2262 /*
2263   Check column rights in given security context
2264 
2265   SYNOPSIS
2266     check_grant_column()
2267     thd                  thread handler
2268     grant                grant information structure
2269     db_name              db name
2270     table_name           table  name
2271     name                 column name
2272     length               column name length
2273     sctx                 security context
2274     want_privilege       wanted privileges
2275 
2276   RETURN
2277     FALSE OK
2278     TRUE  access denied
2279 */
2280 
check_grant_column(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * name,size_t length,Security_context * sctx,ulong want_privilege)2281 bool check_grant_column(THD *thd, GRANT_INFO *grant,
2282                         const char *db_name, const char *table_name,
2283                         const char *name, size_t length,
2284                         Security_context *sctx, ulong want_privilege)
2285 {
2286   GRANT_TABLE *grant_table;
2287   GRANT_COLUMN *grant_column;
2288   DBUG_ENTER("check_grant_column");
2289   DBUG_PRINT("enter", ("table: %s  want_privilege: %lu",
2290                        table_name, want_privilege));
2291 
2292   /*
2293     Make sure that the privilege request is aligned with the overall privileges
2294     granted to and requested for the table.
2295   */
2296   assert(!(want_privilege & ~(grant->want_privilege | grant->privilege)));
2297   // Adjust wanted privileges based on privileges granted to table:
2298   want_privilege&= ~grant->privilege;
2299   if (!want_privilege)
2300     DBUG_RETURN(0);                             // Already checked
2301 
2302   LOCK_grant_read_guard lock(thd);
2303 
2304   /* reload table if someone has modified any grants */
2305 
2306   if (grant->version != grant_version)
2307   {
2308     grant->grant_table=
2309       table_hash_search(sctx->host().str, sctx->ip().str,
2310                         db_name, sctx->priv_user().str,
2311                         table_name, 0);         /* purecov: inspected */
2312     grant->version= grant_version;              /* purecov: inspected */
2313   }
2314   if (!(grant_table= grant->grant_table))
2315     goto err;                                   /* purecov: deadcode */
2316 
2317   grant_column=column_hash_search(grant_table, name, length);
2318   if (grant_column && !(~grant_column->rights & want_privilege))
2319   {
2320     DBUG_RETURN(0);
2321   }
2322 
2323 err:
2324   lock.unlock();
2325   char command[128];
2326   get_privilege_desc(command, sizeof(command), want_privilege);
2327   my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
2328            command,
2329            sctx->priv_user().str,
2330            sctx->host_or_ip().str,
2331            name,
2332            table_name);
2333   DBUG_RETURN(1);
2334 }
2335 
2336 
2337 /*
2338   Check the privileges to a column depending on the type of table.
2339 
2340   SYNOPSIS
2341     check_column_grant_in_table_ref()
2342     thd              thread handler
2343     table_ref        table reference where to check the field
2344     name             name of field to check
2345     length           length of name
2346     want_privilege   wanted privileges
2347 
2348   DESCRIPTION
2349     Check the privileges to a column depending on the type of table
2350     reference where the column is checked. The function provides a
2351     generic interface to check column privileges that hides the
2352     heterogeneity of the column representation - whether it is a view
2353     or a stored table column.
2354 
2355     Notice that this function does not understand that a column from a view
2356     reference must be checked for privileges both in the view and in the
2357     underlying base table (or view) reference. This is the responsibility of
2358     the caller.
2359 
2360   RETURN
2361     FALSE OK
2362     TRUE  access denied
2363 */
2364 
check_column_grant_in_table_ref(THD * thd,TABLE_LIST * table_ref,const char * name,size_t length,ulong want_privilege)2365 bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
2366                                      const char *name, size_t length,
2367                                      ulong want_privilege)
2368 {
2369   GRANT_INFO *grant;
2370   const char *db_name;
2371   const char *table_name;
2372   Security_context *sctx= MY_TEST(table_ref->security_ctx) ?
2373                           table_ref->security_ctx : thd->security_context();
2374 
2375   assert(want_privilege);
2376 
2377   if (table_ref->is_view() || table_ref->field_translation)
2378   {
2379     /* View or derived information schema table. */
2380     ulong view_privs;
2381     grant= &(table_ref->grant);
2382     db_name= table_ref->view_db.str;
2383     table_name= table_ref->view_name.str;
2384     if (table_ref->belong_to_view &&
2385         thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
2386     {
2387       view_privs= get_column_grant(thd, grant, db_name, table_name, name);
2388       if (view_privs & VIEW_ANY_ACL)
2389       {
2390         table_ref->belong_to_view->allowed_show= TRUE;
2391         return FALSE;
2392       }
2393       table_ref->belong_to_view->allowed_show= FALSE;
2394       my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
2395       return TRUE;
2396     }
2397   }
2398   else if (table_ref->nested_join)
2399   {
2400     bool error= FALSE;
2401     List_iterator<TABLE_LIST> it(table_ref->nested_join->join_list);
2402     TABLE_LIST *table;
2403     while (!error && (table= it++))
2404       error|= check_column_grant_in_table_ref(thd, table, name, length,
2405                                               want_privilege);
2406     return error;
2407   }
2408   else
2409   {
2410     /* Normal or temporary table. */
2411     TABLE *table= table_ref->table;
2412     grant= &(table->grant);
2413     db_name= table->s->db.str;
2414     table_name= table->s->table_name.str;
2415   }
2416 
2417   return check_grant_column(thd, grant, db_name, table_name, name,
2418                             length, sctx, want_privilege);
2419 }
2420 
2421 
2422 /**
2423   @brief check if a query can access a set of columns
2424 
2425   @param  thd  the current thread
2426   @param  want_access_arg  the privileges requested
2427   @param  fields an iterator over the fields of a table reference.
2428   @return Operation status
2429     @retval 0 Success
2430     @retval 1 Falure
2431   @details This function walks over the columns of a table reference
2432    The columns may originate from different tables, depending on the kind of
2433    table reference, e.g. join, view.
2434    For each table it will retrieve the grant information and will use it
2435    to check the required access privileges for the fields requested from it.
2436 */
check_grant_all_columns(THD * thd,ulong want_access_arg,Field_iterator_table_ref * fields)2437 bool check_grant_all_columns(THD *thd, ulong want_access_arg,
2438                              Field_iterator_table_ref *fields)
2439 {
2440   Security_context *sctx= thd->security_context();
2441   ulong want_access= want_access_arg;
2442   const char *table_name= NULL;
2443 
2444   const char* db_name;
2445   GRANT_INFO *grant;
2446   /* Initialized only to make gcc happy */
2447   GRANT_TABLE *grant_table= NULL;
2448   /*
2449      Flag that gets set if privilege checking has to be performed on column
2450      level.
2451   */
2452   bool using_column_privileges= FALSE;
2453 
2454   LOCK_grant_read_guard lock(thd);
2455 
2456   for (; !fields->end_of_fields(); fields->next())
2457   {
2458     const char *field_name= fields->name();
2459 
2460     if (table_name != fields->get_table_name())
2461     {
2462       table_name= fields->get_table_name();
2463       db_name= fields->get_db_name();
2464       grant= fields->grant();
2465       /* get a fresh one for each table */
2466       want_access= want_access_arg & ~grant->privilege;
2467       if (want_access)
2468       {
2469         /* reload table if someone has modified any grants */
2470         if (grant->version != grant_version)
2471         {
2472           grant->grant_table=
2473             table_hash_search(sctx->host().str, sctx->ip().str,
2474                               db_name, sctx->priv_user().str,
2475                               table_name, 0);   /* purecov: inspected */
2476           grant->version= grant_version;        /* purecov: inspected */
2477         }
2478 
2479         grant_table= grant->grant_table;
2480         assert (grant_table);
2481       }
2482     }
2483 
2484     if (want_access)
2485     {
2486       GRANT_COLUMN *grant_column=
2487         column_hash_search(grant_table, field_name, strlen(field_name));
2488       if (grant_column)
2489         using_column_privileges= TRUE;
2490       if (!grant_column || (~grant_column->rights & want_access))
2491         goto err;
2492     }
2493   }
2494   return 0;
2495 
2496 err:
2497   lock.unlock();
2498 
2499   char command[128];
2500   get_privilege_desc(command, sizeof(command), want_access);
2501   /*
2502     Do not give an error message listing a column name unless the user has
2503     privilege to see all columns.
2504   */
2505   if (using_column_privileges)
2506     my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
2507              command, sctx->priv_user().str,
2508              sctx->host_or_ip().str, table_name);
2509   else
2510     my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
2511              command,
2512              sctx->priv_user().str,
2513              sctx->host_or_ip().str,
2514              fields->name(),
2515              table_name);
2516   return 1;
2517 }
2518 
2519 
check_grant_db_routine(THD * thd,const char * db,HASH * hash)2520 static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
2521 {
2522   Security_context *sctx= thd->security_context();
2523 
2524   for (uint idx= 0; idx < hash->records; ++idx)
2525   {
2526     GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
2527 
2528     if (strcmp(item->user, sctx->priv_user().str) == 0 &&
2529         strcmp(item->db, db) == 0 &&
2530         item->host.compare_hostname(sctx->host().str,
2531                                     sctx->ip().str))
2532     {
2533       return FALSE;
2534     }
2535   }
2536 
2537   return TRUE;
2538 }
2539 
2540 
2541 /*
2542   Check if a user has the right to access a database
2543   Access is accepted if he has a grant for any table/routine in the database
2544   Return 1 if access is denied
2545 */
2546 
check_grant_db(THD * thd,const char * db)2547 bool check_grant_db(THD *thd,const char *db)
2548 {
2549   Security_context *sctx= thd->security_context();
2550   LEX_CSTRING priv_user= sctx->priv_user();
2551   char helping [NAME_LEN+USERNAME_LENGTH+2];
2552   uint len;
2553   bool error= TRUE;
2554   size_t copy_length;
2555 
2556   /* Added 1 at the end to avoid buffer overflow at strmov()*/
2557   copy_length= ((priv_user.str ? strlen(priv_user.str) : 0) +
2558                 (db ? strlen(db) : 0)) + 1;
2559 
2560   /*
2561     Make sure that my_stpcpy() operations do not result in buffer overflow.
2562   */
2563   if (copy_length >= (NAME_LEN+USERNAME_LENGTH+2))
2564     return 1;
2565 
2566   len= (uint) (my_stpcpy(my_stpcpy(helping, priv_user.str) + 1, db) -
2567                helping) + 1;
2568 
2569   LOCK_grant_read_guard lock(thd);
2570 
2571   for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
2572   {
2573     GRANT_TABLE *grant_table= (GRANT_TABLE*)
2574       my_hash_element(&column_priv_hash,
2575                       idx);
2576     if (len < grant_table->key_length &&
2577         !memcmp(grant_table->hash_key,helping,len) &&
2578         grant_table->host.compare_hostname(sctx->host().str,
2579                                            sctx->ip().str))
2580     {
2581       error= FALSE; /* Found match. */
2582       break;
2583     }
2584   }
2585 
2586   if (error)
2587     error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
2588            check_grant_db_routine(thd, db, &func_priv_hash);
2589 
2590   return error;
2591 }
2592 
2593 
2594 /****************************************************************************
2595   Check routine level grants
2596 
2597   SYNPOSIS
2598    bool check_grant_routine()
2599    thd          Thread handler
2600    want_access  Bits of privileges user needs to have
2601    procs        List of routines to check. The user should have 'want_access'
2602    is_proc      True if the list is all procedures, else functions
2603    no_errors    If 0 then we write an error. The error is sent directly to
2604                 the client
2605 
2606    RETURN
2607      0  ok
2608      1  Error: User did not have the requested privielges
2609 ****************************************************************************/
2610 
check_grant_routine(THD * thd,ulong want_access,TABLE_LIST * procs,bool is_proc,bool no_errors)2611 bool check_grant_routine(THD *thd, ulong want_access,
2612                          TABLE_LIST *procs, bool is_proc, bool no_errors)
2613 {
2614   TABLE_LIST *table;
2615   Security_context *sctx= thd->security_context();
2616   char *user= (char *) sctx->priv_user().str;
2617   char *host= (char *) sctx->priv_host().str;
2618   DBUG_ENTER("check_grant_routine");
2619 
2620   want_access&= ~sctx->master_access();
2621   if (!want_access)
2622     DBUG_RETURN(0);                             // ok
2623 
2624   LOCK_grant_read_guard lock(thd);
2625 
2626   for (table= procs; table; table= table->next_global)
2627   {
2628     GRANT_NAME *grant_proc;
2629     if ((grant_proc= routine_hash_search(host, sctx->ip().str, table->db, user,
2630                                          table->table_name, is_proc, 0)))
2631       table->grant.privilege|= grant_proc->privs;
2632 
2633     if (want_access & ~table->grant.privilege)
2634     {
2635       want_access &= ~table->grant.privilege;
2636       goto err;
2637     }
2638   }
2639   DBUG_RETURN(0);
2640 
2641 err:
2642   lock.unlock();
2643   if (!no_errors)
2644   {
2645     char buff[1024];
2646     const char *command="";
2647     if (table)
2648       strxmov(buff, table->db, ".", table->table_name, NullS);
2649     if (want_access & EXECUTE_ACL)
2650       command= "execute";
2651     else if (want_access & ALTER_PROC_ACL)
2652       command= "alter routine";
2653     else if (want_access & GRANT_ACL)
2654       command= "grant";
2655     my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
2656              command, user, host, table ? buff : "unknown");
2657   }
2658   DBUG_RETURN(1);
2659 }
2660 
2661 
2662 /*
2663   Check if routine has any of the
2664   routine level grants
2665 
2666   SYNPOSIS
2667    bool    check_routine_level_acl()
2668    thd          Thread handler
2669    db           Database name
2670    name         Routine name
2671 
2672   RETURN
2673    0            Ok
2674    1            error
2675 */
2676 
check_routine_level_acl(THD * thd,const char * db,const char * name,bool is_proc)2677 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
2678                              bool is_proc)
2679 {
2680   bool no_routine_acl= 1;
2681   GRANT_NAME *grant_proc;
2682   Security_context *sctx= thd->security_context();
2683 
2684   LOCK_grant_read_guard lock(thd);
2685 
2686   if ((grant_proc= routine_hash_search(sctx->priv_host().str,
2687                                        sctx->ip().str, db,
2688                                        sctx->priv_user().str,
2689                                        name, is_proc, 0)))
2690     no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
2691   return no_routine_acl;
2692 }
2693 
2694 
2695 /*****************************************************************************
2696   Functions to retrieve the grant for a table/column  (for SHOW functions)
2697 *****************************************************************************/
2698 
get_table_grant(THD * thd,TABLE_LIST * table)2699 ulong get_table_grant(THD *thd, TABLE_LIST *table)
2700 {
2701   ulong privilege;
2702   Security_context *sctx= thd->security_context();
2703   const char *db = table->db ? table->db : thd->db().str;
2704   GRANT_TABLE *grant_table;
2705 
2706   LOCK_grant_read_guard lock(thd);
2707 
2708 #ifdef EMBEDDED_LIBRARY
2709   grant_table= NULL;
2710 #else
2711   grant_table= table_hash_search(sctx->host().str,
2712                                  sctx->ip().str, db, sctx->priv_user().str,
2713                                  table->table_name, 0);
2714 #endif /* EMBEDDED_LIBRARY */
2715   table->grant.grant_table=grant_table; // Remember for column test
2716   table->grant.version=grant_version;
2717   if (grant_table)
2718     table->grant.privilege|= grant_table->privs;
2719   privilege= table->grant.privilege;
2720   return privilege;
2721 }
2722 
2723 
2724 /*
2725   Determine the access priviliges for a field.
2726 
2727   SYNOPSIS
2728     get_column_grant()
2729     thd         thread handler
2730     grant       grants table descriptor
2731     db_name     name of database that the field belongs to
2732     table_name  name of table that the field belongs to
2733     field_name  name of field
2734 
2735   DESCRIPTION
2736     The procedure may also modify: grant->grant_table and grant->version.
2737 
2738   RETURN
2739     The access priviliges for the field db_name.table_name.field_name
2740 */
2741 
get_column_grant(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * field_name)2742 ulong get_column_grant(THD *thd, GRANT_INFO *grant,
2743                        const char *db_name, const char *table_name,
2744                        const char *field_name)
2745 {
2746   GRANT_TABLE *grant_table;
2747   GRANT_COLUMN *grant_column;
2748   ulong priv;
2749 
2750   LOCK_grant_read_guard lock(thd);
2751 
2752   /* reload table if someone has modified any grants */
2753   if (grant->version != grant_version)
2754   {
2755     Security_context *sctx= thd->security_context();
2756     grant->grant_table=
2757       table_hash_search(sctx->host().str, sctx->ip().str,
2758                         db_name, sctx->priv_user().str,
2759                         table_name, 0);         /* purecov: inspected */
2760     grant->version= grant_version;              /* purecov: inspected */
2761   }
2762 
2763   if (!(grant_table= grant->grant_table))
2764     priv= grant->privilege;
2765   else
2766   {
2767     grant_column= column_hash_search(grant_table, field_name,
2768                                      strlen(field_name));
2769     if (!grant_column)
2770       priv= (grant->privilege | grant_table->privs);
2771     else
2772       priv= (grant->privilege | grant_table->privs | grant_column->rights);
2773   }
2774   return priv;
2775 }
2776 
show_routine_grants(THD * thd,LEX_USER * lex_user,HASH * hash,const char * type,int typelen,char * buff,int buffsize)2777 static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
2778                                const char *type, int typelen,
2779                                char *buff, int buffsize)
2780 {
2781   uint counter, index;
2782   int error= 0;
2783   Protocol *protocol= thd->get_protocol();
2784   /* Add routine access */
2785   for (index=0 ; index < hash->records ; index++)
2786   {
2787     const char *user, *host;
2788     GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
2789 
2790     if (!(user=grant_proc->user))
2791       user= "";
2792     host= grant_proc->host.get_host();
2793 
2794     /*
2795       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
2796       but make it case-insensitive because that's the way they are
2797       actually applied, and showing fewer privileges than are applied
2798       would be wrong from a security point of view.
2799     */
2800 
2801     if (!strcmp(lex_user->user.str,user) &&
2802         !my_strcasecmp(system_charset_info, lex_user->host.str, host))
2803     {
2804       ulong proc_access= grant_proc->privs;
2805       if (proc_access != 0)
2806       {
2807         String global(buff, buffsize, system_charset_info);
2808         ulong test_access= proc_access & ~GRANT_ACL;
2809 
2810         global.length(0);
2811         global.append(STRING_WITH_LEN("GRANT "));
2812 
2813         if (!test_access)
2814           global.append(STRING_WITH_LEN("USAGE"));
2815         else
2816         {
2817           /* Add specific procedure access */
2818           int found= 0;
2819           ulong j;
2820 
2821           for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
2822           {
2823             if (test_access & j)
2824             {
2825               if (found)
2826                 global.append(STRING_WITH_LEN(", "));
2827               found= 1;
2828               global.append(command_array[counter],command_lengths[counter]);
2829             }
2830           }
2831         }
2832         global.append(STRING_WITH_LEN(" ON "));
2833         global.append(type,typelen);
2834         global.append(' ');
2835         append_identifier(thd, &global, grant_proc->db,
2836                           strlen(grant_proc->db));
2837         global.append('.');
2838         append_identifier(thd, &global, grant_proc->tname,
2839                           strlen(grant_proc->tname));
2840         global.append(STRING_WITH_LEN(" TO '"));
2841         global.append(lex_user->user.str, lex_user->user.length,
2842                       system_charset_info);
2843         global.append(STRING_WITH_LEN("'@'"));
2844         // host and lex_user->host are equal except for case
2845         global.append(host, strlen(host), system_charset_info);
2846         global.append('\'');
2847         if (proc_access & GRANT_ACL)
2848           global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
2849         protocol->start_row();
2850         protocol->store(global.ptr(),global.length(),global.charset());
2851         if (protocol->end_row())
2852         {
2853           error= -1;
2854           break;
2855         }
2856       }
2857     }
2858   }
2859   return error;
2860 }
2861 
2862 
2863 static bool
show_proxy_grants(THD * thd,LEX_USER * user,char * buff,size_t buffsize)2864 show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
2865 {
2866   Protocol *protocol= thd->get_protocol();
2867   int error= 0;
2868 
2869   for (ACL_PROXY_USER *proxy= acl_proxy_users->begin();
2870        proxy != acl_proxy_users->end(); ++proxy)
2871   {
2872     if (proxy->granted_on(user->host.str, user->user.str))
2873     {
2874       String global(buff, buffsize, system_charset_info);
2875       global.length(0);
2876       proxy->print_grant(&global);
2877       protocol->start_row();
2878       protocol->store(global.ptr(), global.length(), global.charset());
2879       if (protocol->end_row())
2880       {
2881         error= -1;
2882         break;
2883       }
2884     }
2885   }
2886   return error;
2887 }
2888 
2889 
2890 /*
2891   Make a clear-text version of the requested privilege.
2892 */
2893 
get_privilege_desc(char * to,uint max_length,ulong access)2894 void get_privilege_desc(char *to, uint max_length, ulong access)
2895 {
2896   uint pos;
2897   char *start=to;
2898   assert(max_length >= 30);                // For end ', ' removal
2899 
2900   if (access)
2901   {
2902     max_length--;                               // Reserve place for end-zero
2903     for (pos=0 ; access ; pos++, access>>=1)
2904     {
2905       if ((access & 1) &&
2906           command_lengths[pos] + (uint) (to-start) < max_length)
2907       {
2908         to= my_stpcpy(to, command_array[pos]);
2909         *to++= ',';
2910         *to++= ' ';
2911       }
2912     }
2913     to--;                                       // Remove end ' '
2914     to--;                                       // Remove end ','
2915   }
2916   *to=0;
2917 }
2918 
2919 
2920 /*
2921   SHOW GRANTS;  Send grants for a user to the client
2922 
2923   IMPLEMENTATION
2924    Send to client grant-like strings depicting user@host privileges
2925 */
2926 
mysql_show_grants(THD * thd,LEX_USER * lex_user)2927 bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
2928 {
2929   ulong want_access;
2930   uint counter,index;
2931   int  error = 0;
2932   ACL_USER *acl_user= NULL;
2933   ACL_DB *acl_db;
2934   char buff[1024];
2935   Protocol *protocol= thd->get_protocol();
2936   DBUG_ENTER("mysql_show_grants");
2937 
2938   if (!initialized)
2939   {
2940     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2941     DBUG_RETURN(TRUE);
2942   }
2943 
2944   LOCK_grant_read_guard lock(thd);
2945   mysql_mutex_lock(&acl_cache->lock);
2946 
2947   acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE);
2948   if (!acl_user)
2949   {
2950     mysql_mutex_unlock(&acl_cache->lock);
2951     lock.unlock();
2952 
2953     my_error(ER_NONEXISTING_GRANT, MYF(0),
2954              lex_user->user.str, lex_user->host.str);
2955     DBUG_RETURN(TRUE);
2956   }
2957 
2958   Item_string *field=new Item_string("",0,&my_charset_latin1);
2959   List<Item> field_list;
2960   field->max_length=1024;
2961   strxmov(buff,"Grants for ",lex_user->user.str,"@",
2962           lex_user->host.str,NullS);
2963   field->item_name.set(buff);
2964   field_list.push_back(field);
2965   if (thd->send_result_metadata(&field_list,
2966                                 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
2967   {
2968     mysql_mutex_unlock(&acl_cache->lock);
2969 
2970     DBUG_RETURN(TRUE);
2971   }
2972 
2973   /* Add first global access grants */
2974   {
2975     String global(buff,sizeof(buff),system_charset_info);
2976     global.length(0);
2977     global.append(STRING_WITH_LEN("GRANT "));
2978 
2979     want_access= acl_user->access;
2980     if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
2981       global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
2982     else if (!(want_access & ~GRANT_ACL))
2983       global.append(STRING_WITH_LEN("USAGE"));
2984     else
2985     {
2986       bool found=0;
2987       ulong j,test_access= want_access & ~GRANT_ACL;
2988       for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
2989       {
2990         if (test_access & j)
2991         {
2992           if (found)
2993             global.append(STRING_WITH_LEN(", "));
2994           found=1;
2995           global.append(command_array[counter],command_lengths[counter]);
2996         }
2997       }
2998     }
2999     global.append (STRING_WITH_LEN(" ON *.* TO '"));
3000     global.append(lex_user->user.str, lex_user->user.length,
3001                   system_charset_info);
3002     global.append (STRING_WITH_LEN("'@'"));
3003     global.append(lex_user->host.str,lex_user->host.length,
3004                   system_charset_info);
3005     global.append ('\'');
3006     if (want_access & GRANT_ACL)
3007       global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
3008     protocol->start_row();
3009     protocol->store(global.ptr(),global.length(),global.charset());
3010     if (protocol->end_row())
3011     {
3012       error= -1;
3013       goto end;
3014     }
3015   }
3016 
3017   /* Add database access */
3018   for (acl_db= acl_dbs->begin(); acl_db != acl_dbs->end(); ++acl_db)
3019   {
3020     const char *user, *host;
3021 
3022     if (!(user=acl_db->user))
3023       user= "";
3024     host= acl_db->host.get_host();
3025 
3026     /*
3027       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
3028       but make it case-insensitive because that's the way they are
3029       actually applied, and showing fewer privileges than are applied
3030       would be wrong from a security point of view.
3031     */
3032 
3033     if (!strcmp(lex_user->user.str,user) &&
3034         !my_strcasecmp(system_charset_info, lex_user->host.str, host))
3035     {
3036       want_access=acl_db->access;
3037       if (want_access)
3038       {
3039         String db(buff,sizeof(buff),system_charset_info);
3040         db.length(0);
3041         db.append(STRING_WITH_LEN("GRANT "));
3042 
3043         if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
3044           db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
3045         else if (!(want_access & ~GRANT_ACL))
3046           db.append(STRING_WITH_LEN("USAGE"));
3047         else
3048         {
3049           int found=0, cnt;
3050           ulong j,test_access= want_access & ~GRANT_ACL;
3051           for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
3052           {
3053             if (test_access & j)
3054             {
3055               if (found)
3056                 db.append(STRING_WITH_LEN(", "));
3057               found = 1;
3058               db.append(command_array[cnt],command_lengths[cnt]);
3059             }
3060           }
3061         }
3062         db.append (STRING_WITH_LEN(" ON "));
3063         append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
3064         db.append (STRING_WITH_LEN(".* TO '"));
3065         db.append(lex_user->user.str, lex_user->user.length,
3066                   system_charset_info);
3067         db.append (STRING_WITH_LEN("'@'"));
3068         // host and lex_user->host are equal except for case
3069         db.append(host, strlen(host), system_charset_info);
3070         db.append ('\'');
3071         if (want_access & GRANT_ACL)
3072           db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
3073         protocol->start_row();
3074         protocol->store(db.ptr(),db.length(),db.charset());
3075         if (protocol->end_row())
3076         {
3077           error= -1;
3078           goto end;
3079         }
3080       }
3081     }
3082   }
3083 
3084   /* Add table & column access */
3085   for (index=0 ; index < column_priv_hash.records ; index++)
3086   {
3087     const char *user, *host;
3088     GRANT_TABLE *grant_table= (GRANT_TABLE*)
3089       my_hash_element(&column_priv_hash, index);
3090 
3091     if (!(user=grant_table->user))
3092       user= "";
3093     host= grant_table->host.get_host();
3094 
3095     /*
3096       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
3097       but make it case-insensitive because that's the way they are
3098       actually applied, and showing fewer privileges than are applied
3099       would be wrong from a security point of view.
3100     */
3101 
3102     if (!strcmp(lex_user->user.str,user) &&
3103         !my_strcasecmp(system_charset_info, lex_user->host.str, host))
3104     {
3105       ulong table_access= grant_table->privs;
3106       if ((table_access | grant_table->cols) != 0)
3107       {
3108         String global(buff, sizeof(buff), system_charset_info);
3109         ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;
3110 
3111         global.length(0);
3112         global.append(STRING_WITH_LEN("GRANT "));
3113 
3114         if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
3115           global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
3116         else if (!test_access)
3117           global.append(STRING_WITH_LEN("USAGE"));
3118         else
3119         {
3120           /* Add specific column access */
3121           int found= 0;
3122           ulong j;
3123 
3124           for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
3125           {
3126             if (test_access & j)
3127             {
3128               if (found)
3129                 global.append(STRING_WITH_LEN(", "));
3130               found= 1;
3131               global.append(command_array[counter],command_lengths[counter]);
3132 
3133               if (grant_table->cols)
3134               {
3135                 uint found_col= 0;
3136                 for (uint col_index=0 ;
3137                      col_index < grant_table->hash_columns.records ;
3138                      col_index++)
3139                 {
3140                   GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
3141                     my_hash_element(&grant_table->hash_columns,col_index);
3142                   if (grant_column->rights & j)
3143                   {
3144                     if (!found_col)
3145                     {
3146                       found_col= 1;
3147                       /*
3148                         If we have a duplicated table level privilege, we
3149                         must write the access privilege name again.
3150                       */
3151                       if (table_access & j)
3152                       {
3153                         global.append(STRING_WITH_LEN(", "));
3154                         global.append(command_array[counter],
3155                                       command_lengths[counter]);
3156                       }
3157                       global.append(STRING_WITH_LEN(" ("));
3158                     }
3159                     else
3160                       global.append(STRING_WITH_LEN(", "));
3161                     global.append(grant_column->column,
3162                                   grant_column->key_length,
3163                                   system_charset_info);
3164                   }
3165                 }
3166                 if (found_col)
3167                   global.append(')');
3168               }
3169             }
3170           }
3171         }
3172         global.append(STRING_WITH_LEN(" ON "));
3173         append_identifier(thd, &global, grant_table->db,
3174                           strlen(grant_table->db));
3175         global.append('.');
3176         append_identifier(thd, &global, grant_table->tname,
3177                           strlen(grant_table->tname));
3178         global.append(STRING_WITH_LEN(" TO '"));
3179         global.append(lex_user->user.str, lex_user->user.length,
3180                       system_charset_info);
3181         global.append(STRING_WITH_LEN("'@'"));
3182         // host and lex_user->host are equal except for case
3183         global.append(host, strlen(host), system_charset_info);
3184         global.append('\'');
3185         if (table_access & GRANT_ACL)
3186           global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
3187         protocol->start_row();
3188         protocol->store(global.ptr(),global.length(),global.charset());
3189         if (protocol->end_row())
3190         {
3191           error= -1;
3192           break;
3193         }
3194       }
3195     }
3196   }
3197 
3198   if (show_routine_grants(thd, lex_user, &proc_priv_hash,
3199                           STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
3200   {
3201     error= -1;
3202     goto end;
3203   }
3204 
3205   if (show_routine_grants(thd, lex_user, &func_priv_hash,
3206                           STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
3207   {
3208     error= -1;
3209     goto end;
3210   }
3211 
3212   if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
3213   {
3214     error= -1;
3215     goto end;
3216   }
3217 
3218 end:
3219   mysql_mutex_unlock(&acl_cache->lock);
3220   lock.unlock();
3221 
3222   my_eof(thd);
3223   DBUG_RETURN(error);
3224 }
3225 
3226 
3227 /*
3228   Revoke all privileges from a list of users.
3229 
3230   SYNOPSIS
3231     mysql_revoke_all()
3232     thd                         The current thread.
3233     list                        The users to revoke all privileges from.
3234 
3235   RETURN
3236     > 0         Error. Error message already sent.
3237     0           OK.
3238     < 0         Error. Error message not yet sent.
3239 */
3240 
mysql_revoke_all(THD * thd,List<LEX_USER> & list)3241 bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
3242 {
3243   uint revoked, is_proc;
3244   int result;
3245   ACL_DB *acl_db;
3246   TABLE_LIST tables[GRANT_TABLES];
3247   bool save_binlog_row_based;
3248   bool transactional_tables;
3249   DBUG_ENTER("mysql_revoke_all");
3250 
3251   /*
3252     This statement will be replicated as a statement, even when using
3253     row-based replication.  The flag will be reset at the end of the
3254     statement.
3255   */
3256   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
3257     thd->clear_current_stmt_binlog_format_row();
3258 
3259   if ((result= open_grant_tables(thd, tables, &transactional_tables)))
3260   {
3261     /* Restore the state of binlog format */
3262     assert(!thd->is_current_stmt_binlog_format_row());
3263     if (save_binlog_row_based)
3264       thd->set_current_stmt_binlog_format_row();
3265     DBUG_RETURN(result != 1);
3266   }
3267 
3268   Partitioned_rwlock_write_guard lock(&LOCK_grant);
3269   mysql_mutex_lock(&acl_cache->lock);
3270 
3271   LEX_USER *lex_user, *tmp_lex_user;
3272   List_iterator <LEX_USER> user_list(list);
3273 
3274   bool is_partial_execution= false;
3275   bool rollback_whole_statement= false;
3276   while ((tmp_lex_user= user_list++))
3277   {
3278     bool is_user_applied= true;
3279     ulong what_to_set= 0;
3280     if (!(lex_user= get_current_user(thd, tmp_lex_user)))
3281     {
3282       result= -1;
3283       continue;
3284     }
3285     if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
3286     {
3287       result= -1;
3288       continue;
3289     }
3290 
3291     /* copy password expire attributes to individual user */
3292     lex_user->alter_status= thd->lex->alter_password;
3293 
3294     int ret= replace_user_table(thd, tables[0].table,
3295                                 lex_user, ~(ulong) 0, true, false,
3296                                 (what_to_set | ACCESS_RIGHTS_ATTR));
3297     if (ret > 0)
3298     {
3299       result= -1;
3300       continue;
3301     }
3302     else if (ret < 0)
3303     {
3304       result= -1;
3305       rollback_whole_statement= true;
3306       break;
3307     }
3308 
3309     /* Remove db access privileges */
3310     /*
3311       Because acl_dbs and column_priv_hash shrink and may re-order
3312       as privileges are removed, removal occurs in a repeated loop
3313       until no more privileges are revoked.
3314      */
3315     do
3316     {
3317       for (revoked= 0, acl_db= acl_dbs->begin(); acl_db != acl_dbs->end(); )
3318       {
3319         const char *user,*host;
3320 
3321         if (!(user=acl_db->user))
3322           user= "";
3323         host= acl_db->host.get_host();
3324 
3325         if (!strcmp(lex_user->user.str,user) &&
3326             !strcmp(lex_user->host.str, host))
3327         {
3328           ret= replace_db_table(tables[1].table, acl_db->db, *lex_user,
3329                                 ~(ulong)0, 1);
3330 
3331           if (ret == 0)
3332           {
3333             /*
3334               Don't increment loop variable as replace_db_table deleted the
3335               current element in acl_dbs.
3336              */
3337             revoked= 1;
3338             continue;
3339           }
3340           else if (ret < 0)
3341           {
3342             result= -1;
3343             rollback_whole_statement= true;
3344             goto user_end;
3345           }
3346           result= -1; // Something went wrong
3347           is_user_applied= false;
3348         }
3349         ++acl_db;
3350       }
3351     } while (revoked);
3352 
3353     /* Remove column access */
3354     do
3355     {
3356       uint counter;
3357       for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
3358       {
3359         const char *user,*host;
3360         GRANT_TABLE *grant_table=
3361           (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
3362         if (!(user=grant_table->user))
3363           user= "";
3364         host= grant_table->host.get_host();
3365 
3366         if (!strcmp(lex_user->user.str,user) &&
3367             !strcmp(lex_user->host.str, host))
3368         {
3369           ret= replace_table_table(thd,grant_table,tables[2].table,*lex_user,
3370                                    grant_table->db,
3371                                    grant_table->tname,
3372                                    ~(ulong)0, 0, 1);
3373           if (ret > 0)
3374           {
3375             result= -1;
3376             is_user_applied= false;
3377           }
3378           else if (ret < 0)
3379           {
3380             result= -1;
3381             rollback_whole_statement= true;
3382             goto user_end;
3383           }
3384           else
3385           {
3386             if (!grant_table->cols)
3387             {
3388               revoked= 1;
3389               continue;
3390             }
3391             List<LEX_COLUMN> columns;
3392             ret= replace_column_table(grant_table,tables[3].table, *lex_user,
3393                                       columns,
3394                                       grant_table->db,
3395                                       grant_table->tname,
3396                                       ~(ulong)0, 1);
3397 
3398             if (ret == 0)
3399             {
3400               revoked= 1;
3401               continue;
3402             }
3403             else if (ret < 0)
3404             {
3405               result= -1;
3406               rollback_whole_statement= true;
3407               goto user_end;
3408             }
3409             result= -1;
3410             is_user_applied= false;
3411           }
3412         }
3413         counter++;
3414       }
3415     } while (revoked);
3416 
3417     /* Remove procedure access */
3418     for (is_proc=0; is_proc<2; is_proc++) do {
3419       HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
3420       uint counter;
3421       for (counter= 0, revoked= 0 ; counter < hash->records ; )
3422       {
3423         const char *user,*host;
3424         GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
3425         if (!(user=grant_proc->user))
3426           user= "";
3427         host= grant_proc->host.get_host();
3428 
3429         if (!strcmp(lex_user->user.str,user) &&
3430             !strcmp(lex_user->host.str, host))
3431         {
3432           ret= replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
3433                                      grant_proc->db,
3434                                      grant_proc->tname,
3435                                      is_proc,
3436                                      ~(ulong)0, 1);
3437 
3438           if (ret == 0)
3439           {
3440             revoked= 1;
3441             continue;
3442           }
3443           else if (ret < 0)
3444           {
3445             result= -1;
3446             rollback_whole_statement= true;
3447             goto user_end;
3448           }
3449           result= -1;  // Something went wrong
3450           is_user_applied= false;
3451         }
3452         counter++;
3453       }
3454     } while (revoked);
3455     if (is_user_applied)
3456       is_partial_execution= true;
3457   }
3458 
3459 user_end:
3460 
3461   mysql_mutex_unlock(&acl_cache->lock);
3462 
3463   DBUG_EXECUTE_IF("force_mysql_revoke_all_fail", {
3464     result= 1;
3465     is_partial_execution= true;
3466     rollback_whole_statement= false;
3467   });
3468 
3469   if (result && !rollback_whole_statement)
3470     my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
3471 
3472   /*
3473     Before ACLs are changed to execute fully or none at all, when
3474     some error happens, write an incident if one or more users are
3475     revoked successfully (it has a partial execution).
3476   */
3477   if (result)
3478   {
3479     if (!rollback_whole_statement || !transactional_tables)
3480     {
3481       if (is_partial_execution)
3482       {
3483         const char* err_msg= "REVOKE failed while revoking all_privileges "
3484                              "from a list of users.";
3485         DEBUG_SYNC(thd, "revoke_all_before_write_incident_to_binlog");
3486         mysql_bin_log.write_incident(thd, true /* need_lock_log=true */,
3487                                      err_msg);
3488       }
3489     }
3490   }
3491   else
3492   {
3493     result= result |
3494       write_bin_log(thd, FALSE, thd->query().str, thd->query().length,
3495                     transactional_tables);
3496   }
3497 
3498   lock.unlock();
3499 
3500   result|=
3501     acl_end_trans_and_close_tables(thd,
3502                                    thd->transaction_rollback_request ||
3503                                    rollback_whole_statement);
3504 
3505   if (!result)
3506     acl_notify_htons(thd, thd->query().str, thd->query().length);
3507 
3508   /* Restore the state of binlog format */
3509   assert(!thd->is_current_stmt_binlog_format_row());
3510   if (save_binlog_row_based)
3511     thd->set_current_stmt_binlog_format_row();
3512 
3513   DBUG_RETURN(result);
3514 }
3515 
3516 
3517 /**
3518   If the defining user for a routine does not exist, then the ACL lookup
3519   code should raise two errors which we should intercept.  We convert the more
3520   descriptive error into a warning, and consume the other.
3521 
3522   If any other errors are raised, then we set a flag that should indicate
3523   that there was some failure we should complain at a higher level.
3524 */
3525 class Silence_routine_definer_errors : public Internal_error_handler
3526 {
3527 public:
Silence_routine_definer_errors()3528   Silence_routine_definer_errors()
3529     : is_grave(false)
3530   {}
3531 
handle_condition(THD * thd,uint sql_errno,const char * sqlstate,Sql_condition::enum_severity_level * level,const char * msg)3532   virtual bool handle_condition(THD *thd,
3533                                 uint sql_errno,
3534                                 const char* sqlstate,
3535                                 Sql_condition::enum_severity_level *level,
3536                                 const char* msg)
3537   {
3538     if (*level == Sql_condition::SL_ERROR)
3539     {
3540       if (sql_errno == ER_NONEXISTING_PROC_GRANT)
3541       {
3542         /* Convert the error into a warning. */
3543         *level= Sql_condition::SL_WARNING;
3544         return true;
3545       }
3546       else
3547         is_grave= true;
3548     }
3549 
3550     return false;
3551   }
3552 
has_errors() const3553   bool has_errors() const { return is_grave; }
3554 
3555 private:
3556   bool is_grave;
3557 };
3558 
3559 
3560 /**
3561   Revoke privileges for all users on a stored procedure.  Use an error handler
3562   that converts errors about missing grants into warnings.
3563 
3564   @param
3565     thd                         The current thread.
3566   @param
3567     db                          DB of the stored procedure
3568   @param
3569     name                        Name of the stored procedure
3570 
3571   @retval
3572     0           OK.
3573   @retval
3574     < 0         Error. Error message not yet sent.
3575 */
3576 
sp_revoke_privileges(THD * thd,const char * sp_db,const char * sp_name,bool is_proc)3577 bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
3578                           bool is_proc)
3579 {
3580   uint counter, revoked;
3581   int result;
3582   TABLE_LIST tables[GRANT_TABLES];
3583   HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
3584   Silence_routine_definer_errors error_handler;
3585   bool save_binlog_row_based;
3586   bool not_used;
3587   DBUG_ENTER("sp_revoke_privileges");
3588 
3589   if ((result= open_grant_tables(thd, tables, &not_used)))
3590     DBUG_RETURN(result != 1);
3591 
3592   /* Be sure to pop this before exiting this scope! */
3593   thd->push_internal_handler(&error_handler);
3594 
3595   Partitioned_rwlock_write_guard lock(&LOCK_grant);
3596   mysql_mutex_lock(&acl_cache->lock);
3597 
3598   /*
3599     This statement will be replicated as a statement, even when using
3600     row-based replication.  The flag will be reset at the end of the
3601     statement.
3602   */
3603   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
3604     thd->clear_current_stmt_binlog_format_row();
3605 
3606   /* Remove procedure access */
3607   bool rollback_whole_statement= false;
3608   do
3609   {
3610     for (counter= 0, revoked= 0 ; counter < hash->records ; )
3611     {
3612       GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
3613       if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
3614           !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
3615       {
3616         LEX_USER lex_user;
3617         lex_user.user.str= grant_proc->user;
3618         lex_user.user.length= strlen(grant_proc->user);
3619         lex_user.host.str= (char *) (grant_proc->host.get_host());
3620         lex_user.host.length= grant_proc->host.get_host_len();
3621 
3622         int ret=
3623           replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
3624                                 grant_proc->db, grant_proc->tname,
3625                                 is_proc, ~(ulong)0, true);
3626         if (ret < 0)
3627         {
3628           rollback_whole_statement= true;
3629           revoked= false;
3630           break;
3631         }
3632         else if (ret == 0)
3633         {
3634           revoked= 1;
3635           continue;
3636         }
3637       }
3638       counter++;
3639     }
3640   } while (revoked);
3641 
3642   mysql_mutex_unlock(&acl_cache->lock);
3643   lock.unlock();
3644 
3645   result|=
3646     acl_end_trans_and_close_tables(thd,
3647                                    thd->transaction_rollback_request ||
3648                                    rollback_whole_statement);
3649 
3650   thd->pop_internal_handler();
3651 
3652   /* Restore the state of binlog format */
3653   assert(!thd->is_current_stmt_binlog_format_row());
3654   if (save_binlog_row_based)
3655     thd->set_current_stmt_binlog_format_row();
3656 
3657   DBUG_RETURN(error_handler.has_errors() || result);
3658 }
3659 
3660 
3661 /**
3662   Grant EXECUTE,ALTER privilege for a stored procedure
3663 
3664   @param thd The current thread.
3665   @param sp_db
3666   @param sp_name
3667   @param is_proc
3668 
3669   @return
3670     @retval FALSE Success
3671     @retval TRUE An error occured. Error message not yet sent.
3672 */
3673 
sp_grant_privileges(THD * thd,const char * sp_db,const char * sp_name,bool is_proc)3674 bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
3675                          bool is_proc)
3676 {
3677   Security_context *sctx= thd->security_context();
3678   LEX_USER *combo;
3679   TABLE_LIST tables[1];
3680   List<LEX_USER> user_list;
3681   bool result;
3682   ACL_USER *au;
3683   Dummy_error_handler error_handler;
3684   DBUG_ENTER("sp_grant_privileges");
3685 
3686   if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
3687     DBUG_RETURN(TRUE);
3688 
3689   combo->user.str= (char *) sctx->priv_user().str;
3690 
3691   mysql_mutex_lock(&acl_cache->lock);
3692 
3693   if ((au= find_acl_user(combo->host.str= (char *) sctx->priv_host().str,
3694                          combo->user.str, false)))
3695     goto found_acl;
3696 
3697   mysql_mutex_unlock(&acl_cache->lock);
3698   DBUG_RETURN(TRUE);
3699 
3700  found_acl:
3701   mysql_mutex_unlock(&acl_cache->lock);
3702 
3703   memset(tables, 0, sizeof(TABLE_LIST));
3704   user_list.empty();
3705 
3706   tables->db= (char*)sp_db;
3707   tables->table_name= tables->alias= (char*)sp_name;
3708 
3709   thd->make_lex_string(&combo->user,
3710                        combo->user.str, strlen(combo->user.str), 0);
3711   thd->make_lex_string(&combo->host,
3712                        combo->host.str, strlen(combo->host.str), 0);
3713 
3714   combo->plugin= EMPTY_CSTR;
3715   combo->auth= EMPTY_CSTR;
3716   combo->uses_identified_by_clause= false;
3717   combo->uses_identified_with_clause= false;
3718   combo->uses_identified_by_password_clause= false;
3719   combo->uses_authentication_string_clause= false;
3720 
3721   if (user_list.push_back(combo))
3722     DBUG_RETURN(TRUE);
3723 
3724   thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
3725   thd->lex->ssl_cipher= thd->lex->x509_subject= thd->lex->x509_issuer= 0;
3726   memset(&thd->lex->mqh, 0, sizeof(thd->lex->mqh));
3727   /* set default values */
3728   thd->lex->alter_password.update_password_expired_column= false;
3729   thd->lex->alter_password.use_default_password_lifetime= true;
3730   thd->lex->alter_password.expire_after_days= 0;
3731   thd->lex->alter_password.update_account_locked_column= false;
3732   thd->lex->alter_password.account_locked= false;
3733 
3734   combo->alter_status= thd->lex->alter_password;
3735 
3736   /*
3737     Only care about whether the operation failed or succeeded
3738     as all errors will be handled later.
3739   */
3740   thd->push_internal_handler(&error_handler);
3741   result= mysql_routine_grant(thd, tables, is_proc, user_list,
3742                               DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
3743   thd->pop_internal_handler();
3744   DBUG_RETURN(result);
3745 }
3746 
3747 
update_schema_privilege(THD * thd,TABLE * table,char * buff,const char * db,const char * t_name,const char * column,size_t col_length,const char * priv,size_t priv_length,const char * is_grantable)3748 static bool update_schema_privilege(THD *thd, TABLE *table, char *buff,
3749                                     const char* db, const char* t_name,
3750                                     const char* column, size_t col_length,
3751                                     const char *priv, size_t priv_length,
3752                                     const char* is_grantable)
3753 {
3754   int i= 2;
3755   CHARSET_INFO *cs= system_charset_info;
3756   restore_record(table, s->default_values);
3757   table->field[0]->store(buff, strlen(buff), cs);
3758   table->field[1]->store(STRING_WITH_LEN("def"), cs);
3759   if (db)
3760     table->field[i++]->store(db, strlen(db), cs);
3761   if (t_name)
3762     table->field[i++]->store(t_name, strlen(t_name), cs);
3763   if (column)
3764     table->field[i++]->store(column, col_length, cs);
3765   table->field[i++]->store(priv, priv_length, cs);
3766   table->field[i]->store(is_grantable, strlen(is_grantable), cs);
3767   return schema_table_store_record(thd, table);
3768 }
3769 
3770 
3771 /*
3772   fill effective privileges for table
3773 
3774   SYNOPSIS
3775     fill_effective_table_privileges()
3776     thd     thread handler
3777     grant   grants table descriptor
3778     db      db name
3779     table   table name
3780 */
3781 
fill_effective_table_privileges(THD * thd,GRANT_INFO * grant,const char * db,const char * table)3782 void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
3783                                      const char *db, const char *table)
3784 {
3785   Security_context *sctx= thd->security_context();
3786   LEX_CSTRING priv_user= sctx->priv_user();
3787   DBUG_ENTER("fill_effective_table_privileges");
3788   DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
3789                        sctx->priv_host().str, (sctx->ip().length ?
3790                        sctx->ip().str : "(NULL)"),
3791                        (priv_user.str ? priv_user.str : "(NULL)"),
3792                        db, table));
3793   /*
3794     This function is not intended for derived tables which doesn't have a
3795     name. If this happens something is wrong.
3796   */
3797   assert(table != 0);
3798   /* --skip-grants */
3799   if (!initialized)
3800   {
3801     DBUG_PRINT("info", ("skip grants"));
3802     grant->privilege= ~NO_ACCESS;             // everything is allowed
3803     DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
3804     DBUG_VOID_RETURN;
3805   }
3806 
3807   /* global privileges */
3808   grant->privilege= sctx->master_access();
3809 
3810   /* db privileges */
3811   grant->privilege|= acl_get(sctx->host().str, sctx->ip().str,
3812                              priv_user.str, db, 0);
3813 
3814   DEBUG_SYNC(thd, "fill_effective_table_privileges");
3815   /* table privileges */
3816   LOCK_grant_read_guard lock(thd);
3817 
3818   if (grant->version != grant_version)
3819   {
3820     grant->grant_table=
3821       table_hash_search(sctx->host().str, sctx->ip().str, db,
3822                         priv_user.str, table, 0);   /* purecov: inspected */
3823     grant->version= grant_version;              /* purecov: inspected */
3824   }
3825   if (grant->grant_table != 0)
3826   {
3827     grant->privilege|= grant->grant_table->privs;
3828   }
3829 
3830   DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
3831   DBUG_VOID_RETURN;
3832 }
3833 
3834 
3835 bool
acl_check_proxy_grant_access(THD * thd,const char * host,const char * user,bool with_grant)3836 acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
3837                              bool with_grant)
3838 {
3839   DBUG_ENTER("acl_check_proxy_grant_access");
3840   DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
3841                       (int) with_grant));
3842   if (!initialized)
3843   {
3844     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3845     DBUG_RETURN(1);
3846   }
3847 
3848   /* replication slave thread can do anything */
3849   if (thd->slave_thread)
3850   {
3851     DBUG_PRINT("info", ("replication slave"));
3852     DBUG_RETURN(FALSE);
3853   }
3854 
3855   /*
3856     one can grant proxy for self to others.
3857     Security context in THD contains two pairs of (user,host):
3858     1. (user,host) pair referring to inbound connection.
3859     2. (priv_user,priv_host) pair obtained from mysql.user table after doing
3860         authnetication of incoming connection.
3861     Privileges should be checked wrt (priv_user, priv_host) tuple, because
3862     (user,host) pair obtained from inbound connection may have different
3863     values than what is actually stored in mysql.user table and while granting
3864     or revoking proxy privilege, user is expected to provide entries mentioned
3865     in mysql.user table.
3866   */
3867   if (!strcmp(thd->security_context()->priv_user().str, user) &&
3868       !my_strcasecmp(system_charset_info, host,
3869                      thd->security_context()->priv_host().str))
3870   {
3871     DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
3872                         thd->security_context()->priv_user().str, user,
3873                         host, thd->security_context()->priv_host().str));
3874     DBUG_RETURN(FALSE);
3875   }
3876 
3877   mysql_mutex_lock(&acl_cache->lock);
3878 
3879   /* check for matching WITH PROXY rights */
3880   for (ACL_PROXY_USER *proxy= acl_proxy_users->begin();
3881        proxy != acl_proxy_users->end(); ++proxy)
3882   {
3883     DEBUG_SYNC(thd, "before_proxy_matches");
3884     if (proxy->matches(thd->security_context()->host().str,
3885                        thd->security_context()->user().str,
3886                        thd->security_context()->ip().str,
3887                        user, FALSE) &&
3888         proxy->get_with_grant())
3889     {
3890       DBUG_PRINT("info", ("found"));
3891       mysql_mutex_unlock(&acl_cache->lock);
3892       DBUG_RETURN(FALSE);
3893     }
3894   }
3895 
3896   mysql_mutex_unlock(&acl_cache->lock);
3897   my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
3898            thd->security_context()->user().str,
3899            thd->security_context()->host_or_ip().str);
3900   DBUG_RETURN(TRUE);
3901 }
3902 
3903 
3904 #else /* NO_EMBEDDED_ACCESS_CHECKS */
3905 
3906 /****************************************************************************
3907  Dummy wrappers when we don't have any access checks
3908 ****************************************************************************/
3909 
check_routine_level_acl(THD * thd,const char * db,const char * name,bool is_proc)3910 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
3911                              bool is_proc)
3912 {
3913   return FALSE;
3914 }
3915 
3916 
3917 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
3918 
3919 
fill_schema_user_privileges(THD * thd,TABLE_LIST * tables,Item * cond)3920 int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
3921 {
3922 #ifndef NO_EMBEDDED_ACCESS_CHECKS
3923   int error= 0;
3924   ACL_USER *acl_user;
3925   ulong want_access;
3926   char buff[USERNAME_LENGTH + HOSTNAME_LENGTH + 3];
3927   TABLE *table= tables->table;
3928   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
3929                                       NULL, NULL, 1, 1);
3930   const char *curr_host= thd->security_context()->priv_host_name();
3931   DBUG_ENTER("fill_schema_user_privileges");
3932 
3933   if (!initialized)
3934     DBUG_RETURN(0);
3935   mysql_mutex_lock(&acl_cache->lock);
3936 
3937   for (acl_user= acl_users->begin(); acl_user != acl_users->end(); ++acl_user)
3938   {
3939     const char *user,*host, *is_grantable="YES";
3940     if (!(user=acl_user->user))
3941       user= "";
3942     host= acl_user->host.get_host();
3943 
3944     if (no_global_access &&
3945         (strcmp(thd->security_context()->priv_user().str, user) ||
3946          my_strcasecmp(system_charset_info, curr_host, host)))
3947       continue;
3948 
3949     want_access= acl_user->access;
3950     if (!(want_access & GRANT_ACL))
3951       is_grantable= "NO";
3952 
3953     strxmov(buff,"'",user,"'@'",host,"'",NullS);
3954     if (!(want_access & ~GRANT_ACL))
3955     {
3956       if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
3957                                   STRING_WITH_LEN("USAGE"), is_grantable))
3958       {
3959         error= 1;
3960         goto err;
3961       }
3962     }
3963     else
3964     {
3965       uint priv_id;
3966       ulong j,test_access= want_access & ~GRANT_ACL;
3967       for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
3968       {
3969         if (test_access & j)
3970         {
3971           if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
3972                                       command_array[priv_id],
3973                                       command_lengths[priv_id], is_grantable))
3974           {
3975             error= 1;
3976             goto err;
3977           }
3978         }
3979       }
3980     }
3981   }
3982 err:
3983   mysql_mutex_unlock(&acl_cache->lock);
3984 
3985   DBUG_RETURN(error);
3986 #else
3987   return(0);
3988 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
3989 }
3990 
3991 
fill_schema_schema_privileges(THD * thd,TABLE_LIST * tables,Item * cond)3992 int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
3993 {
3994 #ifndef NO_EMBEDDED_ACCESS_CHECKS
3995   int error= 0;
3996   ACL_DB *acl_db;
3997   ulong want_access;
3998   char buff[USERNAME_LENGTH + HOSTNAME_LENGTH + 3];
3999   TABLE *table= tables->table;
4000   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
4001                                       NULL, NULL, 1, 1);
4002   const char *curr_host= thd->security_context()->priv_host_name();
4003   DBUG_ENTER("fill_schema_schema_privileges");
4004 
4005   if (!initialized)
4006     DBUG_RETURN(0);
4007   mysql_mutex_lock(&acl_cache->lock);
4008 
4009   for (acl_db= acl_dbs->begin(); acl_db != acl_dbs->end(); ++acl_db)
4010   {
4011     const char *user, *host, *is_grantable="YES";
4012 
4013     if (!(user=acl_db->user))
4014       user= "";
4015     host= acl_db->host.get_host();
4016 
4017     if (no_global_access &&
4018         (strcmp(thd->security_context()->priv_user().str, user) ||
4019          my_strcasecmp(system_charset_info, curr_host, host)))
4020       continue;
4021 
4022     want_access=acl_db->access;
4023     if (want_access)
4024     {
4025       if (!(want_access & GRANT_ACL))
4026       {
4027         is_grantable= "NO";
4028       }
4029       strxmov(buff,"'",user,"'@'",host,"'",NullS);
4030       if (!(want_access & ~GRANT_ACL))
4031       {
4032         if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0,
4033                                     0, STRING_WITH_LEN("USAGE"), is_grantable))
4034         {
4035           error= 1;
4036           goto err;
4037         }
4038       }
4039       else
4040       {
4041         int cnt;
4042         ulong j,test_access= want_access & ~GRANT_ACL;
4043         for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
4044           if (test_access & j)
4045           {
4046             if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0, 0,
4047                                         command_array[cnt], command_lengths[cnt],
4048                                         is_grantable))
4049             {
4050               error= 1;
4051               goto err;
4052             }
4053           }
4054       }
4055     }
4056   }
4057 err:
4058   mysql_mutex_unlock(&acl_cache->lock);
4059 
4060   DBUG_RETURN(error);
4061 #else
4062   return (0);
4063 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
4064 }
4065 
4066 
fill_schema_table_privileges(THD * thd,TABLE_LIST * tables,Item * cond)4067 int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
4068 {
4069 #ifndef NO_EMBEDDED_ACCESS_CHECKS
4070   int error= 0;
4071   uint index;
4072   char buff[USERNAME_LENGTH + HOSTNAME_LENGTH + 3];
4073   TABLE *table= tables->table;
4074   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
4075                                       NULL, NULL, 1, 1);
4076   const char *curr_host= thd->security_context()->priv_host_name();
4077   DBUG_ENTER("fill_schema_table_privileges");
4078 
4079   LOCK_grant_read_guard lock(thd);
4080 
4081   for (index=0 ; index < column_priv_hash.records ; index++)
4082   {
4083     const char *user, *host, *is_grantable= "YES";
4084     GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
4085                                                           index);
4086     if (!(user=grant_table->user))
4087       user= "";
4088     host= grant_table->host.get_host();
4089 
4090     if (no_global_access &&
4091         (strcmp(thd->security_context()->priv_user().str, user) ||
4092          my_strcasecmp(system_charset_info, curr_host, host)))
4093       continue;
4094 
4095     ulong table_access= grant_table->privs;
4096     if (table_access)
4097     {
4098       ulong test_access= table_access & ~GRANT_ACL;
4099       /*
4100         We should skip 'usage' privilege on table if
4101         we have any privileges on column(s) of this table
4102       */
4103       if (!test_access && grant_table->cols)
4104         continue;
4105       if (!(table_access & GRANT_ACL))
4106         is_grantable= "NO";
4107 
4108       strxmov(buff, "'", user, "'@'", host, "'", NullS);
4109       if (!test_access)
4110       {
4111         if (update_schema_privilege(thd, table, buff, grant_table->db,
4112                                     grant_table->tname, 0, 0,
4113                                     STRING_WITH_LEN("USAGE"), is_grantable))
4114         {
4115           error= 1;
4116           goto err;
4117         }
4118       }
4119       else
4120       {
4121         ulong j;
4122         int cnt;
4123         for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
4124         {
4125           if (test_access & j)
4126           {
4127             if (update_schema_privilege(thd, table, buff, grant_table->db,
4128                                         grant_table->tname, 0, 0,
4129                                         command_array[cnt],
4130                                         command_lengths[cnt], is_grantable))
4131             {
4132               error= 1;
4133               goto err;
4134             }
4135           }
4136         }
4137       }
4138     }
4139   }
4140 err:
4141 
4142   DBUG_RETURN(error);
4143 #else
4144   return (0);
4145 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
4146 }
4147 
4148 
fill_schema_column_privileges(THD * thd,TABLE_LIST * tables,Item * cond)4149 int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
4150 {
4151 #ifndef NO_EMBEDDED_ACCESS_CHECKS
4152   int error= 0;
4153   uint index;
4154   char buff[USERNAME_LENGTH + HOSTNAME_LENGTH + 3];
4155   TABLE *table= tables->table;
4156   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
4157                                       NULL, NULL, 1, 1);
4158   const char *curr_host= thd->security_context()->priv_host_name();
4159   DBUG_ENTER("fill_schema_table_privileges");
4160 
4161   LOCK_grant_read_guard lock(thd);
4162 
4163   for (index=0 ; index < column_priv_hash.records ; index++)
4164   {
4165     const char *user, *host, *is_grantable= "YES";
4166     GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
4167                                                           index);
4168     if (!(user=grant_table->user))
4169       user= "";
4170     host= grant_table->host.get_host();
4171 
4172     if (no_global_access &&
4173         (strcmp(thd->security_context()->priv_user().str, user) ||
4174          my_strcasecmp(system_charset_info, curr_host, host)))
4175       continue;
4176 
4177     ulong table_access= grant_table->cols;
4178     if (table_access != 0)
4179     {
4180       if (!(grant_table->privs & GRANT_ACL))
4181         is_grantable= "NO";
4182 
4183       ulong test_access= table_access & ~GRANT_ACL;
4184       strxmov(buff, "'", user, "'@'", host, "'", NullS);
4185       if (!test_access)
4186         continue;
4187       else
4188       {
4189         ulong j;
4190         int cnt;
4191         for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
4192         {
4193           if (test_access & j)
4194           {
4195             for (uint col_index=0 ;
4196                  col_index < grant_table->hash_columns.records ;
4197                  col_index++)
4198             {
4199               GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
4200                 my_hash_element(&grant_table->hash_columns,col_index);
4201               if ((grant_column->rights & j) && (table_access & j))
4202               {
4203                 if (update_schema_privilege(thd, table, buff, grant_table->db,
4204                                             grant_table->tname,
4205                                             grant_column->column,
4206                                             grant_column->key_length,
4207                                             command_array[cnt],
4208                                             command_lengths[cnt], is_grantable))
4209                 {
4210                   error= 1;
4211                   goto err;
4212                 }
4213               }
4214             }
4215           }
4216         }
4217       }
4218     }
4219   }
4220 err:
4221 
4222   DBUG_RETURN(error);
4223 #else
4224   return (0);
4225 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
4226 }
4227 
4228 #ifndef NO_EMBEDDED_ACCESS_CHECKS
4229 bool
is_privileged_user_for_credential_change(THD * thd)4230 is_privileged_user_for_credential_change(THD *thd)
4231 {
4232 #ifdef HAVE_REPLICATION
4233   if (thd->slave_thread)
4234     return true;
4235 #endif /* HAVE_REPLICATION */
4236   return (!check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) ||
4237           thd->security_context()->check_access(CREATE_USER_ACL, false));
4238 }
4239 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
4240 
4241 /**
4242   Check if user has enough privileges for execution of SHOW statement,
4243   which was converted to query to one of I_S tables.
4244 
4245   @param thd    Thread context.
4246   @param table  Table list element for I_S table to be queried..
4247 
4248   @retval FALSE - Success.
4249   @retval TRUE  - Failure.
4250 */
4251 
check_show_access(THD * thd,TABLE_LIST * table)4252 static bool check_show_access(THD *thd, TABLE_LIST *table)
4253 {
4254 #ifndef NO_EMBEDDED_ACCESS_CHECKS
4255   switch (get_schema_table_idx(table->schema_table)) {
4256   case SCH_SCHEMATA:
4257     return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
4258       check_global_access(thd, SHOW_DB_ACL);
4259 
4260   case SCH_TABLE_NAMES:
4261   case SCH_TABLES:
4262   case SCH_VIEWS:
4263   case SCH_TRIGGERS:
4264   case SCH_EVENTS:
4265   {
4266     const char *dst_db_name= table->schema_select_lex->db;
4267 
4268     assert(dst_db_name);
4269 
4270     if (check_access(thd, SELECT_ACL, dst_db_name,
4271                      &thd->col_access, NULL, FALSE, FALSE))
4272       return TRUE;
4273 
4274     if (!thd->col_access && check_grant_db(thd, dst_db_name))
4275     {
4276       my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
4277                thd->security_context()->priv_user().str,
4278                thd->security_context()->priv_host().str,
4279                dst_db_name);
4280       return TRUE;
4281     }
4282 
4283     return FALSE;
4284   }
4285 
4286   case SCH_COLUMNS:
4287   case SCH_STATISTICS:
4288   {
4289     TABLE_LIST *dst_table;
4290     dst_table= table->schema_select_lex->table_list.first;
4291 
4292     assert(dst_table);
4293 
4294     /*
4295       Open temporary tables to be able to detect them during privilege check.
4296     */
4297     if (open_temporary_tables(thd, dst_table))
4298       return TRUE;
4299 
4300     if (check_access(thd, SELECT_ACL, dst_table->db,
4301                      &dst_table->grant.privilege,
4302                      &dst_table->grant.m_internal,
4303                      FALSE, FALSE))
4304           return TRUE; /* Access denied */
4305 
4306     /*
4307       Check_grant will grant access if there is any column privileges on
4308       all of the tables thanks to the fourth parameter (bool show_table).
4309     */
4310     if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE))
4311       return TRUE; /* Access denied */
4312 
4313     close_thread_tables(thd);
4314     dst_table->table= NULL;
4315 
4316     /* Access granted */
4317     return FALSE;
4318   }
4319   default:
4320     break;
4321   }
4322 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
4323   return FALSE;
4324 }
4325 
4326 
4327 /**
4328   check for global access and give descriptive error message if it fails.
4329 
4330   @param thd			Thread handler
4331   @param want_access		Use should have any of these global rights
4332 
4333   @warning
4334     One gets access right if one has ANY of the rights in want_access.
4335     This is useful as one in most cases only need one global right,
4336     but in some case we want to check if the user has SUPER or
4337     REPL_CLIENT_ACL rights.
4338 
4339   @retval
4340     0	ok
4341   @retval
4342     1	Access denied.  In this case an error is sent to the client
4343 */
4344 
check_global_access(THD * thd,ulong want_access)4345 bool check_global_access(THD *thd, ulong want_access)
4346 {
4347   DBUG_ENTER("check_global_access");
4348 #ifndef NO_EMBEDDED_ACCESS_CHECKS
4349   char command[128];
4350   if (thd->security_context()->check_access(want_access, true))
4351     DBUG_RETURN(0);
4352   get_privilege_desc(command, sizeof(command), want_access);
4353   my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
4354   DBUG_RETURN(1);
4355 #else
4356   DBUG_RETURN(0);
4357 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
4358 }
4359 
4360 
4361 /**
4362   Checks foreign key's parent table access.
4363 
4364   @param thd              [in]  Thread handler
4365   @param child_table_db   [in]  Database of child table
4366   @param create_info      [in]  Create information (like MAX_ROWS, ENGINE or
4367                                 temporary table flag)
4368   @param alter_info       [in]  Initial list of columns and indexes for the
4369                                 table to be created
4370 
4371   @retval
4372    false  ok.
4373   @retval
4374    true	  error or access denied. Error is sent to client in this case.
4375 */
check_fk_parent_table_access(THD * thd,const char * child_table_db,HA_CREATE_INFO * create_info,Alter_info * alter_info)4376 bool check_fk_parent_table_access(THD *thd,
4377                                   const char *child_table_db,
4378                                   HA_CREATE_INFO *create_info,
4379                                   Alter_info *alter_info)
4380 {
4381   Key *key;
4382   List_iterator<Key> key_iterator(alter_info->key_list);
4383   handlerton *db_type= create_info->db_type ? create_info->db_type :
4384                                              ha_default_handlerton(thd);
4385 
4386   // Return if engine does not support Foreign key Constraint.
4387   if (!ha_check_storage_engine_flag(db_type, HTON_SUPPORTS_FOREIGN_KEYS))
4388     return false;
4389 
4390   while ((key= key_iterator++))
4391   {
4392     if (key->type == KEYTYPE_FOREIGN)
4393     {
4394       TABLE_LIST parent_table;
4395       bool is_qualified_table_name;
4396       Foreign_key *fk_key= (Foreign_key *)key;
4397       LEX_STRING db_name;
4398       LEX_STRING table_name= { (char *) fk_key->ref_table.str,
4399                                fk_key->ref_table.length };
4400 
4401       // Check if tablename is valid or not.
4402       assert(table_name.str != NULL);
4403       if (check_table_name(table_name.str, table_name.length, false))
4404       {
4405         my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name.str);
4406         return true;
4407       }
4408 
4409       if (fk_key->ref_db.str)
4410       {
4411         is_qualified_table_name= true;
4412         db_name.str= (char *) thd->memdup(fk_key->ref_db.str,
4413                                           fk_key->ref_db.length+1);
4414         db_name.length= fk_key->ref_db.length;
4415 
4416         // Check if database name is valid or not.
4417         if (fk_key->ref_db.str && check_and_convert_db_name(&db_name, false))
4418           return true;
4419       }
4420       else
4421       {
4422         /*
4423           If database name for parent table is not specified explicitly
4424           SEs assume that it is the same as database name of child table.
4425           We do the same here.
4426         */
4427         is_qualified_table_name= false;
4428         db_name.str= const_cast<char*>(child_table_db);
4429         db_name.length= strlen(child_table_db);
4430       }
4431 
4432       // if lower_case_table_names is set then convert tablename to lower case.
4433       if (lower_case_table_names)
4434       {
4435         table_name.str= (char *) thd->memdup(fk_key->ref_table.str,
4436                                              fk_key->ref_table.length+1);
4437         table_name.length= my_casedn_str(files_charset_info, table_name.str);
4438       }
4439 
4440       parent_table.init_one_table(db_name.str, db_name.length,
4441                                   table_name.str, table_name.length,
4442                                   table_name.str, TL_IGNORE);
4443 
4444       /*
4445        Check if user has REFERENCES_ACL privilege at table level on
4446        "parent_table".
4447        Having privilege on any of the parent_table column is not
4448        enough so checking whether user has REFERENCES_ACL privilege
4449        at table level here.
4450       */
4451       if ((check_access(thd, REFERENCES_ACL, parent_table.db,
4452                         &parent_table.grant.privilege,
4453                         &parent_table.grant.m_internal, false, true) ||
4454            check_grant(thd, REFERENCES_ACL, &parent_table, false, 1, true)) ||
4455           (parent_table.grant.privilege & REFERENCES_ACL) == 0)
4456       {
4457         if (is_qualified_table_name)
4458         {
4459           const size_t qualified_table_name_len= NAME_LEN + 1 + NAME_LEN + 1;
4460           char *qualified_table_name=
4461             (char *) thd->alloc(qualified_table_name_len);
4462 
4463           my_snprintf(qualified_table_name, qualified_table_name_len, "%s.%s",
4464                       db_name.str, table_name.str);
4465           table_name.str= qualified_table_name;
4466         }
4467 
4468         my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
4469                  "REFERENCES",
4470                  thd->security_context()->priv_user().str,
4471                  thd->security_context()->host_or_ip().str,
4472                  table_name.str);
4473 
4474         return true;
4475       }
4476     }
4477   }
4478 
4479   return false;
4480 }
4481 
4482 
4483 /**
4484   For LOCK TABLES on a view checks if user in which context view is executed
4485   or user that has initiated this operation has SELECT and LOCK TABLES
4486   privileges on one of its underlying tables.
4487 
4488   @param [in]   thd                   Thread context.
4489   @param [in]   tbl                   Table list element for underlying table
4490                                       on which we check privilege.
4491   @param [out]  fake_lock_tables_acl  Set to true if table in question is one
4492                                       of special I_S or P_S tables on which
4493                                       nobody can get LOCK TABLES privilege.
4494                                       So to preserve compatibility with dump
4495                                       tools we need to fake this privilege.
4496                                       Set to false otherwise.
4497 
4498   @retval false   Success.
4499   @retval true    Access denied. Error has been reported.
4500 */
check_lock_view_underlying_table_access(THD * thd,TABLE_LIST * tbl,bool * fake_lock_tables_acl)4501 bool check_lock_view_underlying_table_access(THD *thd, TABLE_LIST *tbl,
4502                                              bool *fake_lock_tables_acl)
4503 {
4504   ulong want_access= SELECT_ACL | LOCK_TABLES_ACL;
4505   *fake_lock_tables_acl= false;
4506 
4507   /*
4508     I_S and P_S tables require special handling of LOCK TABLES privilege
4509     in this case.
4510     On the one hand we don't grant this privileges on I_S and read-only/
4511     truncatable-only P_S tables to anyone. So normally you can't lock
4512     them directly using LOCK TABLES.
4513     On the other hand we allow creation of views which reference these
4514     tables. And mysqldump/pump tools routinely lock views using LOCK
4515     TABLES just to dump their definition in default mode. So refusing
4516     locking of such views will break mysqldump/pump. It will also break
4517     user scenarios in when views on top of I_S/P_S tables are locked along
4518     with other tables by LOCK TABLES, so they are accessible under LOCK
4519     TABLES mode. So we simply skip LOCK TABLES privilege check for I_S and
4520     read-only/ truncatable-only P_S tables. However, we report the fact to
4521     the caller, so it won't acquire strong metadata locks in this case,
4522     which can be considered privilege escalation.
4523   */
4524   const ACL_internal_schema_access *schema_access=
4525       get_cached_schema_access(&tbl->grant.m_internal, tbl->db);
4526   if (schema_access)
4527   {
4528     ulong dummy= 0;
4529     switch (schema_access->check(LOCK_TABLES_ACL, &dummy))
4530     {
4531     case ACL_INTERNAL_ACCESS_DENIED:
4532       *fake_lock_tables_acl= true;
4533       // Fall through.
4534     case ACL_INTERNAL_ACCESS_GRANTED:
4535       want_access&= ~LOCK_TABLES_ACL;
4536       break;
4537     case ACL_INTERNAL_ACCESS_CHECK_GRANT:
4538       const ACL_internal_table_access *table_access= get_cached_table_access(
4539           &tbl->grant.m_internal, tbl->db, tbl->table_name);
4540       if (table_access)
4541       {
4542         switch (table_access->check(LOCK_TABLES_ACL, &dummy))
4543         {
4544         case ACL_INTERNAL_ACCESS_DENIED:
4545           *fake_lock_tables_acl= true;
4546           // Fall through.
4547         case ACL_INTERNAL_ACCESS_GRANTED:
4548           want_access&= ~LOCK_TABLES_ACL;
4549           break;
4550         case ACL_INTERNAL_ACCESS_CHECK_GRANT:
4551           break;
4552         }
4553       }
4554       break;
4555     }
4556   }
4557 
4558   if (!check_single_table_access(thd, want_access, tbl, true))
4559     return false;
4560 
4561   /*
4562     As it was mentioned earlier mysqldump/pump tools routinely lock
4563     views just to dump their definition. This is supposed to work even
4564     for views with (temporarily) invalid definer. To avoid breaking
4565     this scenario we allow locking of view not only when user which
4566     security context will be used for its execution has LOCK TABLES
4567     and SELECT privileges on its underlying tbales, but also when
4568     the user which originally requested LOCK TABLES has the similar
4569     privileges on its underlying tables (which is likely to be the
4570     case for users invoking mysqldump/pump).
4571   */
4572   Security_context *save_security_ctx= tbl->security_ctx;
4573   tbl->security_ctx= NULL;
4574   bool top_user_has_privs=
4575       !check_single_table_access(thd, want_access, tbl, true);
4576   tbl->security_ctx= save_security_ctx;
4577 
4578   if (top_user_has_privs)
4579     return false;
4580 
4581   my_error(ER_VIEW_INVALID, MYF(0), tbl->belong_to_view->get_db_name(),
4582            tbl->belong_to_view->get_table_name());
4583   return true;
4584 }
4585