1 /* Copyright (c) 2000, 2020, 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
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 #include "sql/auth/sql_authorization.h"
24 
25 #include <limits.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <algorithm>
30 #include <boost/concept/usage.hpp>
31 #include <boost/function.hpp>
32 #include <boost/graph/adjacency_iterator.hpp>
33 #include <boost/graph/adjacency_list.hpp>
34 #include <boost/graph/breadth_first_search.hpp>
35 #include <boost/graph/filtered_graph.hpp>
36 #include <boost/graph/graph_traits.hpp>
37 #include <boost/graph/graphml.hpp>
38 #include <boost/graph/named_function_params.hpp>
39 #include <boost/graph/properties.hpp>
40 #include <boost/iterator/iterator_facade.hpp>
41 #include <boost/move/utility_core.hpp>
42 #include <boost/property_map/dynamic_property_map.hpp>
43 #include <boost/property_map/property_map.hpp>
44 #include <boost/range/irange.hpp>
45 #include <boost/smart_ptr/make_shared_object.hpp>
46 #include <boost/smart_ptr/shared_ptr.hpp>
47 #include <boost/tuple/tuple.hpp>
48 #include <cstdlib>
49 #include <iterator>
50 #include <map>
51 #include <memory>
52 #include <set>
53 #include <sstream>
54 #include <string>
55 #include <tuple>
56 #include <type_traits>
57 #include <unordered_map>
58 #include <unordered_set>
59 #include <utility>
60 #include <vector>
61 
62 #include "lex_string.h"
63 #include "m_ctype.h"
64 #include "m_string.h"
65 #include "map_helpers.h"
66 #include "mf_wcomp.h"
67 #include "my_alloc.h"
68 #include "my_compiler.h"
69 #include "my_dbug.h"
70 #include "my_inttypes.h"
71 #include "my_loglevel.h"
72 #include "my_macros.h"
73 #include "my_sqlcommand.h"
74 #include "my_sys.h"
75 #include "mysql/components/services/log_builtins.h"
76 #include "mysql/components/services/log_shared.h"
77 #include "mysql/mysql_lex_string.h"
78 #include "mysql/plugin_audit.h"
79 #include "mysql/psi/mysql_mutex.h"
80 #include "mysql/service_mysql_alloc.h"
81 #include "mysql_com.h"
82 #include "mysqld_error.h"
83 #include "prealloced_array.h"
84 #include "sql/auth/auth_acls.h"
85 #include "sql/auth/auth_common.h"
86 #include "sql/auth/auth_internal.h"
87 #include "sql/auth/auth_utility.h"
88 #include "sql/auth/dynamic_privilege_table.h"
89 #include "sql/auth/partial_revokes.h"
90 #include "sql/auth/role_tables.h"
91 #include "sql/auth/roles.h"
92 #include "sql/auth/sql_auth_cache.h"
93 #include "sql/auth/sql_security_ctx.h"
94 #include "sql/auth/sql_user_table.h"
95 #include "sql/current_thd.h"
96 #include "sql/dd/dd_table.h"  // dd::table_exists
97 #include "sql/debug_sync.h"
98 #include "sql/derror.h"        /* ER_THD */
99 #include "sql/error_handler.h" /* error_handler */
100 #include "sql/field.h"
101 #include "sql/handler.h"
102 #include "sql/item.h"
103 #include "sql/key_spec.h" /* Key_spec */
104 #include "sql/mdl.h"
105 #include "sql/mysqld.h" /* lower_case_table_names */
106 #include "sql/nested_join.h"
107 #include "sql/protocol.h"
108 #include "sql/sp.h"         /* sp_exist_routines */
109 #include "sql/sql_admin.h"  // enum role_enum
110 #include "sql/sql_alter.h"
111 #include "sql/sql_audit.h"
112 #include "sql/sql_base.h"  /* open_and_lock_tables */
113 #include "sql/sql_class.h" /* THD */
114 #include "sql/sql_connect.h"
115 #include "sql/sql_error.h"
116 #include "sql/sql_lex.h"
117 #include "sql/sql_list.h"
118 #include "sql/sql_parse.h"   /* get_current_user */
119 #include "sql/sql_rewrite.h" /* Grant_params */
120 #include "sql/sql_show.h"    /* append_identifier */
121 #include "sql/sql_view.h"    /* VIEW_ANY_ACL */
122 #include "sql/strfunc.h"
123 #include "sql/system_variables.h"
124 #include "sql/table.h"
125 #include "sql/thd_raii.h"
126 #include "sql_string.h"
127 #include "template_utils.h"
128 #include "thr_lock.h"
129 #include "violite.h"
130 
131 /**
132    @file sql_authorization.cc
133 
134    AUTHORIZATION CODE
135 
136 */
137 
138 /**
139   @page AUTHORIZATION_PAGE Authorization IDs, roles and users
140 
141   @section AUTHORIZATION_ID Authentication ID
142   @subsection AUTH_ID_DEFINITION Definition
143  Each row in the mysql.user table is identified by a user and host tuple. This
144  tuple is the authorization ID.
145  A client can authenticate with an authorization ID and a password. The ID is
146  then referred to as a user or user name.
147 
148   @section AUTHORIZATION_PRIVILEGES Privileges ID
149   @subsection AUTH_PRIV_DEFINITION Definition
150  A privilege ID is a named token which can be granted to an authorization ID.
151 
152  A privilege can either be effective or not effective. An effective privilege is
153  a privilege which used in a session to evaluate if a particular operation is
154  allowed or not. All effective privileges are granted or inherited but not all
155  privileges are effective.
156 
157   @section AUTHORIZATION_ROLES Roles
158   @subsection AUTH_ROLES_DEFINITION Definition
159  A role is an authorization ID which can be granted to another authorization ID
160  by forming an directed edge between them in the role graph where every vertex
161  is a unique authorization ID. When the effective privilege is calculated, all
162  connected roles are visited according to their edge direction and their
163  corresponding granted privileges are aggregated.
164 
165   @subsection ACTIVE_ROLE Active roles
166  A role can either be active or inactive. Active roles are kept in a thread
167  local list which exists solely for the lifetime of a client session. Granted
168  roles can be made active by
169    1) a SET ROLE statement,
170    2) after authentication if the role is a default role,
171    3) after authentication if the global variable
172 opt_always_activate_roles_on_login is set to true.
173 
174   Example: To set the grated role ``team``\@``%`` as an active role, after
175     authentication, execute: SET ROLE team
176 
177   @subsection DEFAULT_ROLE Default roles
178  Each authorization ID has a list of default roles. Default roles belonging to
179  an authorization ID are made into active roles after authentication iff they
180  are granted to this ID. If the list of default roles is empty then no roles are
181  made active after authentication unless the client sets a
182  SET ROLE statement.
183 
184   @subsection MANDATORY_ROLE Mandatory roles
185  A mandatory role is an authorization ID which is implicitly granted to every
186  other authorization ID which has authenticated, regardless if this role has
187  been previously granted or not. Mandatory roles are specified in a global
188  variable. It's not required that the specified list maps to any existing
189  authorization ID but if there's no previous authorization ID then no mandatory
190  role can be granted. Mandatory roles are processed sequentially as any other
191  granted role when the effective privilege of an authorization ID needs to be
192  calculated iff they are active.
193 
194  @section AUTHORIZATION_CACHE The effective privilege cache
195  @subsection OVERVIEW Overview
196  To avoid recalculating the effective privilege at every step the result is
197  saved into a cache (See Acl_cache ). The key to this cache is
198  formed by concatenating authorization ID, the active roles and the version ID
199  of the cache.
200 
201  The cache is a lockless hash storage and each element is assembled using
202  read-only operations on the shared role graph.
203  @see get_privilege_access_maps
204 
205  @section AUTHORIZATION_SHOW_GRANTS SHOW GRANTS
206 
207  The statements @code SHOW GRANT @endcode shows all effective privileges using
208  the currently active roles for the current user.
209 
210  The statement @code SHOW GRANT FOR x USING y @endcode is used for listing the
211  effective privilege for x given y as active roles. If If y isn't specified then
212  no roles are used. If x isn't specified then the current_user() is used.
213  Mandatory roles are always excluded from the list of granted roles when this
214  statement is used.
215 
216  Example: To show the privilege for a role using no roles:
217  @code SHOW GRANTS FOR x. @endcode
218 
219  SHOW-statements does not use the privilege cache and the effective privilege is
220  recalculated on every execution.
221  @see mysql_show_grants
222 
223  To show the role graph use @code SELECT roles_graphml() @endcode
224 
225  To investigate the role graph use the built in XML functions or the
226  mysql.role_edges table.
227 
228  */
229 
230 namespace {
231 /**
232   Class to handle sanity checks for GRANT ... AS ... statement
233 */
234 
235 class Grant_validator {
236  public:
Grant_validator(THD * thd,const char * db,const List<LEX_USER> & user_list,ulong rights,bool revoke,const List<LEX_CSTRING> & dynamic_privilege,bool grant_all,LEX_GRANT_AS * grant_as,TABLE * dynamic_priv_table)237   explicit Grant_validator(THD *thd, const char *db,
238                            const List<LEX_USER> &user_list, ulong rights,
239                            bool revoke,
240                            const List<LEX_CSTRING> &dynamic_privilege,
241                            bool grant_all, LEX_GRANT_AS *grant_as,
242                            TABLE *dynamic_priv_table)
243       : m_thd(thd),
244         m_db(db),
245         m_user_list(user_list),
246         m_rights(rights),
247         m_revoke(revoke),
248         m_dynamic_privilege(dynamic_privilege),
249         m_grant_all(grant_all),
250         m_grant_as(grant_as),
251         m_dynamic_priv_table(dynamic_priv_table),
252         m_restore(false),
253         m_backup(nullptr) {}
254   ~Grant_validator();
255   bool validate();
256 
257  private:
258   bool mask_and_return_error();
259   bool validate_system_user_privileges();
260   bool validate_dynamic_privileges();
261   bool validate_and_process_grant_as();
262 
263  private:
264   THD *m_thd;
265   const char *m_db;
266   const List<LEX_USER> &m_user_list;
267   ulong m_rights;
268   bool m_revoke;
269   const List<LEX_CSTRING> &m_dynamic_privilege;
270   bool m_grant_all;
271   LEX_GRANT_AS *m_grant_as;
272   TABLE *m_dynamic_priv_table;
273   bool m_restore;
274   Security_context *m_backup;
275   Security_context m_security_context;
276 };
277 
278 /*
279   Destructor. Restores original security context.
280 */
~Grant_validator()281 Grant_validator::~Grant_validator() {
282   if (m_restore)
283     m_thd->security_context()->restore_security_context(m_thd, m_backup);
284 }
285 
286 /**
287   Helper function to mask specific error with generic one.
288 
289   @returns true always.
290 */
mask_and_return_error()291 bool Grant_validator::mask_and_return_error() {
292   DBUG_TRACE;
293   /* Restore security context */
294   if (m_restore)
295     m_thd->security_context()->restore_security_context(m_thd, m_backup);
296   m_restore = false;
297   /*
298     Any error set before this point may potentially give away
299     information about user and/or role. So, clear any error
300     that may have been raised and replace it with a generic error.
301   */
302   m_thd->get_stmt_da()->reset_diagnostics_area();
303   my_error(ER_UKNOWN_AUTH_ID_OR_ACCESS_DENIED_FOR_GRANT_AS, MYF(0));
304   return true;
305 }
306 
307 /**
308   Perform sanity checks for GRANT ... AS ...
309 
310   @returns status of checks
311     @retval false Success. Security context may have been changed
312     @retval true  Failure. Error has been raised.
313 */
validate_and_process_grant_as()314 bool Grant_validator::validate_and_process_grant_as() {
315   DBUG_TRACE;
316 
317   if (m_grant_as == nullptr || !m_grant_as->grant_as_used) return false;
318 
319   LEX_USER *user = get_current_user(m_thd, m_grant_as->user);
320   if (user == nullptr) return mask_and_return_error();
321 
322   /* Change security context */
323   if (m_security_context.change_security_context(m_thd, user->user, user->host,
324                                                  nullptr, &m_backup, true))
325     return mask_and_return_error();
326 
327   m_restore = true;
328 
329   Roles::Role_activation role_activation(m_thd, m_thd->security_context(),
330                                          m_grant_as->role_type,
331                                          m_grant_as->role_list, false);
332 
333   if (role_activation.activate()) return mask_and_return_error();
334 
335   /* Compare restrictions */
336   Restrictions this_restrictions = m_thd->security_context()->restrictions();
337   Restrictions other_restrictions = m_backup->restrictions();
338   if (this_restrictions.has_more_db_restrictions(other_restrictions, m_rights))
339     return mask_and_return_error();
340 
341   return false;
342 }
343 
344 /**
345   Validate that if grantee has SYSTEM_USER privileges, current user has it too.
346 
347   @returns status of the check
348     @retval false Success
349     @retval true  Current user lacks SYSTEM_USER privilege
350 */
validate_system_user_privileges()351 bool Grant_validator::validate_system_user_privileges() {
352   DBUG_TRACE;
353   if (check_system_user_privilege(m_thd, m_user_list)) return true;
354   return false;
355 }
356 
357 /**
358   Permission and sanity checks for dynamic privileges.
359 
360   We check:
361   1. Dynamic privilege is granted at *.* level
362   2. Current user's ability to grant dynamic privilege
363   3. SYSTEM_USER is not granted to mandatory roles
364 
365   @returns status of checks
366     @retval false Success
367     @retval true  Error validating dynamic privileges
368 */
validate_dynamic_privileges()369 bool Grant_validator::validate_dynamic_privileges() {
370   DBUG_TRACE;
371 
372   /* Dynamic privileges are allowed only for global grants */
373   if (m_db && m_db != any_db && m_dynamic_privilege.elements > 0) {
374     String privs;
375     bool comma = false;
376     for (const LEX_CSTRING &priv : m_dynamic_privilege) {
377       if (comma) privs.append(",");
378       privs.append(priv.str, priv.length);
379       comma = true;
380     }
381     my_error(ER_ILLEGAL_PRIVILEGE_LEVEL, MYF(0), privs.c_ptr());
382     return true;
383   }
384 
385   /* Sanity checks for dynamic privileges */
386   if (!m_db && (m_dynamic_privilege.elements > 0 || m_grant_all)) {
387     LEX_CSTRING *priv;
388     Update_dynamic_privilege_table update_table(m_thd, m_dynamic_priv_table);
389     List<LEX_CSTRING> *privileges_to_check;
390     if (m_grant_all) {
391       /*
392         Copy all currently available dynamic privileges to the list of
393         dynamic privileges to grant.
394       */
395       privileges_to_check = new (m_thd->mem_root) List<LEX_CSTRING>;
396       iterate_all_dynamic_privileges(m_thd, [&](const char *str) {
397         LEX_CSTRING *new_str = (LEX_CSTRING *)m_thd->alloc(sizeof(LEX_CSTRING));
398         new_str->str = str;
399         new_str->length = strlen(str);
400         privileges_to_check->push_back(new_str);
401         return false;
402       });
403     } else
404       privileges_to_check =
405           &const_cast<List<LEX_CSTRING> &>(m_dynamic_privilege);
406     List_iterator<LEX_CSTRING> priv_it(*privileges_to_check);
407     bool error = false;
408     Security_context *sctx = m_thd->security_context();
409     while ((priv = priv_it++) && !error) {
410       /*
411         Privilege to grant dynamic privilege to others is granted if the user
412         either has super user privileges (currently UPDATE_ACL on mysql.*) or
413         if the user has a GRANT_OPTION on the specific dynamic privilege he
414         wants to grant.
415         Note that this is different than the rules which apply for other
416         privileges since for them the GRANT OPTION applies on a privilege
417         scope level (ie global, db or table level).
418         From a user POV it might appear confusing that some privileges are
419         more strictly associated with GRANT OPTION than others, but this
420         choice is made to preserve back compatibility while also paving way
421         for future improvements where all privileges objects have their own
422         grant option.
423       */
424       if (check_access(m_thd, UPDATE_ACL, consts::mysql.c_str(), nullptr,
425                        nullptr, true, true) &&
426           !sctx->has_global_grant(priv->str, priv->length).second) {
427         my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "GRANT OPTION");
428         return true;
429       }
430 
431       if (!m_revoke) {
432         // Do not grant SYSTEM_USER privilege to a mandatory role
433         if (consts::system_user.compare(priv->str) == 0) {
434           std::vector<Role_id> mandatory_roles;
435           get_mandatory_roles(&mandatory_roles);
436           List_iterator<LEX_USER> str_list(
437               const_cast<List<LEX_USER> &>(m_user_list));
438           LEX_USER *user, *target_user;
439           while ((target_user = str_list++)) {
440             if (!(user = get_current_user(m_thd, target_user))) {
441               my_error(ER_OUTOFMEMORY, MYF(0), sizeof(LEX_USER));
442               return true;
443             }
444             Auth_id_ref auth_id = create_authid_from(user);
445             for (const auto &rid : mandatory_roles) {
446               if (rid == auth_id) {
447                 my_error(ER_CANNOT_GRANT_SYSTEM_PRIV_TO_MANDATORY_ROLE, MYF(0),
448                          auth_id.first.str, auth_id.second.str, priv->str);
449                 return true;
450               }
451             }
452           }
453         }
454       }
455     }
456   }
457 
458   return false;
459 }
460 
461 /**
462   Umbrella method to perform validation
463 
464   A possible side effect of this method is that active security context of the
465   session may have been changed. This is true if GRANT ... AS ... is used.
466 
467   @returns status of checks
468     @retval false Success
469     @retval true  Error found during validation
470 */
validate()471 bool Grant_validator::validate() {
472   DBUG_TRACE;
473   if (validate_system_user_privileges()) return true;
474   if (validate_dynamic_privileges()) return true;
475 
476   /*
477     This must be the last check because it may change
478     the active security context of a thread.
479   */
480   if (validate_and_process_grant_as()) return true;
481   return false;
482 }
483 
484 /**
485   The dynamic privilege is probed in the global map that keeps track of
486   dynamic privileges registered with server. The policy is that
487   - Plugin/Component may register a privilege ID
488   - Any privilege ID that exist in mysql.global_grants is a valid privilege ID
489 
490   This method assumes that caller must have acquired the necessory ACL_LOCK.
491 
492   @param [in] privilege Privilege to be checked in the dynamic privilege map
493 
494   @retval true Privilege is registered
495   @retval false Otherwise
496 */
is_dynamic_privilege_registered(const std::string & privilege)497 bool is_dynamic_privilege_registered(const std::string &privilege) {
498   if (get_dynamic_privilege_register()->find(privilege) !=
499       get_dynamic_privilege_register()->end()) {
500     return true;
501   }
502   return false;
503 }
504 }  // namespace
505 
506 Granted_roles_graph *g_granted_roles = nullptr;
507 Role_index_map *g_authid_to_vertex = nullptr;
508 static char g_active_dummy_user[] = "active dummy user";
509 extern bool initialized;
510 extern Default_roles *g_default_roles;
511 typedef boost::graph_traits<Granted_roles_graph>::adjacency_iterator
512     Role_adjacency_iterator;
513 User_to_dynamic_privileges_map *g_dynamic_privileges_map = nullptr;
514 const char *any_db = "*any*";  // Special symbol for check_access
515 
516 static bool check_routine_level_acl(THD *thd, const char *db, const char *name,
517                                     bool is_proc);
518 void get_granted_roles(Role_vertex_descriptor &v,
519                        List_of_granted_roles *granted_roles);
520 
521 /**
522   This utility function is used by revoke_role() and remove_all_granted_roles()
523   for removing a specific edge from the role graph.
524   @param thd Thread handler
525   @param authid_role The role which should be revoked
526   @param authid_user The user who will get its role revoked
527   @param [out] user_vert The vertex descriptor of the user
528   @param [out] role_vert The vertex descriptor of the role
529 
530   @return Success state
531     @retval true No such user
532     @retval false User was removed
533 */
534 
revoke_role_helper(THD * thd MY_ATTRIBUTE ((unused)),std::string & authid_role,std::string & authid_user,Role_vertex_descriptor * user_vert,Role_vertex_descriptor * role_vert)535 bool revoke_role_helper(THD *thd MY_ATTRIBUTE((unused)),
536                         std::string &authid_role, std::string &authid_user,
537                         Role_vertex_descriptor *user_vert,
538                         Role_vertex_descriptor *role_vert) {
539   DBUG_TRACE;
540   DBUG_ASSERT(assert_acl_cache_write_lock(thd));
541 
542   Role_index_map::iterator it = g_authid_to_vertex->find(authid_user);
543   if (it == g_authid_to_vertex->end()) {
544     // No such user
545     return true;
546   } else
547     *user_vert = it->second;
548 
549   it = g_authid_to_vertex->find(authid_role);
550   if (it == g_authid_to_vertex->end()) {
551     // No such role
552     return true;
553   } else
554     *role_vert = it->second;
555 
556   boost::remove_edge(*user_vert, *role_vert, *g_granted_roles);
557 
558   return false;
559 }
560 
561 /**
562   This utility function checks for the connecting vertices of the role
563   descriptor(authid node) and updates the role flag of the corresponding
564   ACL user. If there are no incoming edges to this authid node then this
565   is not a role id anymore. It assumes that acl user and role descriptor
566   are, valid and passed correctly.
567 
568   @param [in] role_vert The role vertex descriptor
569   @param [in,out] acl_user The acl role
570 
571 */
update_role_flag_of_acl_user(const Role_vertex_descriptor & role_vert,ACL_USER * acl_user)572 void static update_role_flag_of_acl_user(
573     const Role_vertex_descriptor &role_vert, ACL_USER *acl_user) {
574   degree_s_t count = boost::in_degree(role_vert, *g_granted_roles);
575   acl_user->is_role = (count > 0) ? true : false;
576 }
577 
578 /**
579  Used by mysql_revoke_role() for revoking a specified role from a specified
580  user.
581 
582  @param thd Thread handler
583  @param role The role which will be revoked
584  @param user The user who will get its role revoked
585 
586 */
revoke_role(THD * thd,ACL_USER * role,ACL_USER * user)587 void revoke_role(THD *thd, ACL_USER *role, ACL_USER *user) {
588   std::string authid_role = create_authid_str_from(role);
589   std::string authid_user = create_authid_str_from(user);
590   Role_vertex_descriptor user_vert;
591   Role_vertex_descriptor role_vert;
592   if (!revoke_role_helper(thd, authid_role, authid_user, &user_vert,
593                           &role_vert)) {
594     update_role_flag_of_acl_user(role_vert, role);
595   }
596 }
597 
598 /**
599     Since the gap in the vertex vector was removed all the vertex descriptors
600     has changed. As a consequence we now need to rebuild the authid_to_vertex
601     index.
602 */
rebuild_vertex_index(THD * thd MY_ATTRIBUTE ((unused)))603 void rebuild_vertex_index(THD *thd MY_ATTRIBUTE((unused))) {
604   DBUG_ASSERT(assert_acl_cache_write_lock(thd));
605   for (auto &acl_user : *acl_users) {
606     create_role_vertex(&acl_user);
607   }
608   g_authid_to_vertex->clear();
609   boost::graph_traits<Granted_roles_graph>::vertex_iterator vert_it, vert_end;
610   boost::tie(vert_it, vert_end) = boost::vertices(*g_granted_roles);
611   for (; vert_it != vert_end; ++vert_it) {
612     ACL_USER acl_user =
613         boost::get(boost::vertex_acl_user_t(),
614                    *g_granted_roles)[boost::vertex(*vert_it, *g_granted_roles)];
615     if (acl_user.user == g_active_dummy_user) {
616       (*g_authid_to_vertex)["root"] = *vert_it;
617     } else {
618       std::string authid = create_authid_str_from(&acl_user);
619       (*g_authid_to_vertex)[authid] = *vert_it;
620     }
621   }
622 }
623 
drop_role(THD * thd,TABLE * edge_table,TABLE * defaults_table,const Auth_id_ref & authid_user)624 bool drop_role(THD *thd, TABLE *edge_table, TABLE *defaults_table,
625                const Auth_id_ref &authid_user) {
626   DBUG_TRACE;
627   bool error = false;
628   std::vector<ACL_USER> users;
629   DBUG_ASSERT(assert_acl_cache_write_lock(thd));
630   std::string authid_user_str = create_authid_str_from(authid_user);
631   Role_index_map::iterator it;
632 
633   if ((it = g_authid_to_vertex->find(authid_user_str)) !=
634       g_authid_to_vertex->end()) {
635     /* Fetch source vertex details */
636     ACL_USER source_acl_user = boost::get(
637         boost::vertex_acl_user_t(),
638         *g_granted_roles)[boost::vertex(it->second, *g_granted_roles)];
639     Auth_id_ref source_user = create_authid_from(&source_acl_user);
640 
641     /*
642       Lambda function that drops all adjacent edges(if exists) from the
643       source_user present in the role_edges table and, keep track of
644       target acl user.
645       It assumes all the paramaters and captures, are valid and sane.
646     */
647     auto modify_role_edges = [&thd, &edge_table, &error,
648                               &source_user](const ACL_USER &target_acl_user) {
649       Auth_id_ref target_user = create_authid_from(&target_acl_user);
650       error = modify_role_edges_in_table(thd, edge_table, source_user,
651                                          target_user, false, true);
652       error |= modify_role_edges_in_table(thd, edge_table, target_user,
653                                           source_user, false, true);
654     };
655 
656     /* Fetch the neighboring vertices from the outgoing edges */
657     out_edge_itr_t oute_itr, oute_end;
658     boost::tie(oute_itr, oute_end) =
659         boost::out_edges(it->second, *g_granted_roles);
660     for (; oute_itr != oute_end; ++oute_itr) {
661       ACL_USER target_acl_user = boost::get(
662           boost::vertex_acl_user_t(),
663           *g_granted_roles)[boost::target(*oute_itr, *g_granted_roles)];
664       modify_role_edges(target_acl_user);
665       users.push_back(target_acl_user);
666     }
667 
668     /* Fetch the neighboring vertices from the incoming edges */
669     in_edge_itr_t ine_itr, ine_end;
670     boost::tie(ine_itr, ine_end) =
671         boost::in_edges(it->second, *g_granted_roles);
672     for (; ine_itr != ine_end; ++ine_itr) {
673       ACL_USER target_acl_user = boost::get(
674           boost::vertex_acl_user_t(),
675           *g_granted_roles)[boost::source(*ine_itr, *g_granted_roles)];
676       modify_role_edges(target_acl_user);
677     }
678 
679     /* Remove this vertex from the graph (along with its edges) */
680     DBUG_PRINT("info", ("Removing %s from graph and rebuild the index.",
681                         authid_user_str.c_str()));
682     /*
683       We clear all edges connecting this vertex but we avoid removing it
684       from the graph at this time as it would invalidate the vertex
685       descriptors and we would have to rebuild all indexes. For now it is
686       enough to remove the index entry. As the roles  are reloaded from the
687       tables the dropped roles will disappear.
688     */
689     boost::clear_vertex(it->second, *g_granted_roles);
690 
691     /*
692       If the role authid does not have any incoming edges then update
693       the role flag of corresponding ACL role.
694     */
695     for (auto &&user_itr : users) {
696       Role_index_map::iterator role_it =
697           g_authid_to_vertex->find(create_authid_str_from(&user_itr));
698       if (role_it != g_authid_to_vertex->end()) {
699         ACL_USER *acl_role =
700             find_acl_user(user_itr.host.get_host(), user_itr.user, true);
701         DBUG_ASSERT(acl_role != nullptr);
702         update_role_flag_of_acl_user(role_it->second, acl_role);
703       }
704     }
705   }
706   // Remove all default role policies assigned to this authid.
707   clear_default_roles(thd, defaults_table, authid_user, nullptr);
708   // Remove all default role policies in which this authid is a default role.
709   std::vector<Default_roles::iterator> delete_policies;
710   for (auto policy = g_default_roles->begin(); policy != g_default_roles->end();
711        ++policy) {
712     if (policy->second == authid_user) {
713       delete_policies.push_back(policy);
714     }
715   }
716   for (auto &&policy : delete_policies) {
717     modify_default_roles_in_table(thd, defaults_table,
718                                   create_authid_from(policy->first),
719                                   create_authid_from(policy->second), true);
720     g_default_roles->erase(policy);
721   }
722   return error;
723 }
724 
725 /**
726   Used by @ref mysql_drop_user. Will drop all
727   @param thd             THD handle
728   @param edge_table      Handle to table that stores role grants
729   @param defaults_table  Handle to table that stores default role information
730   @param user_name       User being dropped
731 
732   @retval true An error occurred
733   @retval false Success
734 */
revoke_all_roles_from_user(THD * thd,TABLE * edge_table,TABLE * defaults_table,LEX_USER * user_name)735 bool revoke_all_roles_from_user(THD *thd, TABLE *edge_table,
736                                 TABLE *defaults_table, LEX_USER *user_name) {
737   List_of_granted_roles granted_roles;
738   get_granted_roles(user_name, &granted_roles);
739   Auth_id_ref user_name_authid = create_authid_from(user_name);
740   bool error = drop_role(thd, edge_table, defaults_table, user_name_authid);
741   return error;
742 }
743 
744 /**
745   If possible, it will revoke all roles and default roles from user_from and
746   set them for user_to instead.
747 
748   @param thd Thread handle
749   @param table A table handler
750   @param user_from The name of the ACL_USER which will be renamed.
751   @param [out] granted_roles A list of roles that were successfully revoked.
752 
753   @return success state
754     @retval true En error occurred
755     @retval false Successful
756 */
revoke_all_granted_roles(THD * thd,TABLE * table,LEX_USER * user_from,List_of_granted_roles * granted_roles)757 bool revoke_all_granted_roles(THD *thd, TABLE *table, LEX_USER *user_from,
758                               List_of_granted_roles *granted_roles) {
759   DBUG_TRACE;
760   std::string authid_user = create_authid_str_from(user_from);
761   Role_index_map::iterator it;
762   if ((it = g_authid_to_vertex->find(authid_user)) ==
763       g_authid_to_vertex->end()) {
764     /* The user from wasn't in the role graph index; nothing to do. */
765     return true;
766   }
767 
768   get_granted_roles(it->second, granted_roles);
769   Role_vertex_descriptor user_vert;
770   Role_vertex_descriptor role_vert;
771   bool errors = false;
772   for (auto &&ref : *granted_roles) {
773     std::string role_id_str;
774     ref.first.auth_str(&role_id_str);
775     std::string user_from_str = create_authid_str_from(user_from);
776     Auth_id_ref role_id = create_authid_from(ref.first);
777     errors = modify_role_edges_in_table(thd, table, role_id,
778                                         {user_from->user, user_from->host},
779                                         ref.second, true);
780     if (errors) break;
781     /*
782       If the role is revoked then update the flag in the
783       corresponding ACL authid.
784     */
785     if (!revoke_role_helper(thd, role_id_str, user_from_str, &user_vert,
786                             &role_vert)) {
787       ACL_USER *acl_role = find_acl_user(ref.first.host().c_str(),
788                                          ref.first.user().c_str(), ref.second);
789       DBUG_ASSERT(acl_role != nullptr);
790       update_role_flag_of_acl_user(role_vert, acl_role);
791     }
792   }
793   return errors;
794 }
795 
is_role_id(LEX_USER * authid)796 bool is_role_id(LEX_USER *authid) {
797   ACL_USER *acl_user = find_acl_user(authid->host.str, authid->user.str, true);
798   if (acl_user == nullptr) return false;
799   return acl_user->is_role;
800 }
801 
802 /**
803   Grants a single role to a single user. The change is made to the in-memory
804   roles graph and not persistent.
805 
806   @see mysql_grant_role
807 
808   @param role A pointer to the role to be granted
809   @param user A pointer to the user which will be granted
810   @param with_admin_opt True if the user should have the ability to pass on the
811     granted role to another authorization id.
812 */
grant_role(ACL_USER * role,const ACL_USER * user,bool with_admin_opt)813 void grant_role(ACL_USER *role, const ACL_USER *user, bool with_admin_opt) {
814   DBUG_TRACE;
815   bool is_added;
816   std::string authid_role = create_authid_str_from(role);
817   std::string authid_user = create_authid_str_from(user);
818   Role_vertex_descriptor user_vert, role_vert;
819   Role_index_map::iterator it;
820 
821   if ((it = g_authid_to_vertex->find(authid_user)) ==
822       g_authid_to_vertex->end()) {
823     user_vert = boost::add_vertex(*g_granted_roles);
824     g_authid_to_vertex->insert(make_pair(authid_user, user_vert));
825   } else
826     user_vert = it->second;
827 
828   if ((it = g_authid_to_vertex->find(authid_role)) ==
829       g_authid_to_vertex->end()) {
830     role_vert = boost::add_vertex(*g_granted_roles);
831     g_authid_to_vertex->insert(make_pair(authid_role, role_vert));
832   } else
833     role_vert = it->second;
834 
835   boost::property_map<Granted_roles_graph, boost::vertex_name_t>::type
836       user_pname,
837       role_pname;
838   user_pname = boost::get(boost::vertex_name_t(), *g_granted_roles);
839   boost::put(user_pname, user_vert, authid_user);
840   role_pname = boost::get(boost::vertex_name_t(), *g_granted_roles);
841   boost::put(role_pname, role_vert, authid_role);
842 
843   boost::property_map<Granted_roles_graph, boost::vertex_acl_user_t>::type
844       user_pacl_user,
845       role_pacl_user;
846   user_pacl_user = boost::get(boost::vertex_acl_user_t(), *g_granted_roles);
847   boost::put(user_pacl_user, user_vert, *user);
848   role_pacl_user = boost::get(boost::vertex_acl_user_t(), *g_granted_roles);
849   boost::put(role_pacl_user, role_vert, *role);
850 
851   Role_edge_descriptor edge;
852   tie(edge, is_added) = add_edge(user_vert, role_vert, *g_granted_roles);
853 
854   boost::property_map<Granted_roles_graph, boost::edge_capacity_t>::type
855       edge_colors;
856   edge_colors = boost::get(boost::edge_capacity_t(), *g_granted_roles);
857   boost::put(edge_colors, edge, (with_admin_opt ? 1 : 0));
858   role->is_role = true;
859 }
860 
861 /**
862   Helper function for create_roles_vertices. Creates a vertex in the role
863   graph and associate it with an ACL_USER. If the ACL_USER already exists in
864   the vertex-to-acl-user index then we ignore this request.
865 
866   @param role_acl_user The acial user to be mapped to a vertex.
867 */
create_role_vertex(ACL_USER * role_acl_user)868 void create_role_vertex(ACL_USER *role_acl_user) {
869   Role_vertex_descriptor role_vertex;
870   Role_index_map::iterator it;
871   std::string key = create_authid_str_from(role_acl_user);
872   if ((it = g_authid_to_vertex->find(key)) == g_authid_to_vertex->end()) {
873     role_vertex = boost::add_vertex(*g_granted_roles);
874     boost::property_map<Granted_roles_graph, boost::vertex_acl_user_t>::type
875         root_prop;
876     root_prop = boost::get(boost::vertex_acl_user_t(), *g_granted_roles);
877     boost::put(root_prop, role_vertex, *role_acl_user);
878     boost::property_map<Granted_roles_graph, boost::vertex_name_t>::type
879         role_pname;
880     role_pname = boost::get(boost::vertex_name_t(), *g_granted_roles);
881     boost::put(role_pname, role_vertex, key);
882     g_authid_to_vertex->insert(std::make_pair(key, role_vertex));
883   }
884 }
885 
886 /**
887   Renames a user in the mysql.role_edge and the mysql.default_roles
888   tables. user_to must already exist in the acl_user cache, but user_from
889   may not as long as it exist in the role graph.
890 
891   @param thd Thread handler
892   @param edge_table An open table handle for mysql.edge_mysql
893   @param defaults_table An open table handle for mysql.default_roles
894   @param user_from The user to rename
895   @param user_to The target user name
896 
897   @see mysql_rename_user
898 
899   @retval true An error occurred
900   @retval false Success
901 */
902 
roles_rename_authid(THD * thd,TABLE * edge_table,TABLE * defaults_table,LEX_USER * user_from,LEX_USER * user_to)903 bool roles_rename_authid(THD *thd, TABLE *edge_table, TABLE *defaults_table,
904                          LEX_USER *user_from, LEX_USER *user_to) {
905   DBUG_ASSERT(assert_acl_cache_write_lock(thd));
906   ACL_USER *acl_user_to =
907       find_acl_user(user_to->host.str, user_to->user.str, true);
908   if (acl_user_to == nullptr) {
909     /* The target user doesn't exist yet? */
910     return true;
911   }
912   /* Update default roles */
913   std::vector<Role_id> old_roles;
914   Auth_id_ref authid_user_from = create_authid_from(user_from);
915   clear_default_roles(thd, defaults_table, authid_user_from, &old_roles);
916   List_of_auth_id_refs new_default_role_ref;
917   for (auto &&role : old_roles) {
918     Auth_id_ref authid = create_authid_from(role);
919     new_default_role_ref.push_back(authid);
920   }
921   bool ret = alter_user_set_default_roles(thd, defaults_table, user_to,
922                                           new_default_role_ref);
923 
924   if (ret) {
925     String warning;
926     append_identifier(thd, &warning, user_from->user.str,
927                       user_from->user.length);
928     append_identifier(thd, &warning, user_from->host.str,
929                       user_from->host.length);
930     LogErr(WARNING_LEVEL, ER_SQL_AUTHOR_DEFAULT_ROLES_FAIL, warning.c_ptr());
931     ret = false;
932   }
933 
934   List_of_granted_roles granted_roles;
935   ret = revoke_all_granted_roles(thd, edge_table, user_from, &granted_roles);
936   if (!ret) {
937     for (auto &&ref : granted_roles) {
938       ACL_USER *acl_role = find_acl_user(ref.first.host().c_str(),
939                                          ref.first.user().c_str(), ref.second);
940       if (acl_role == nullptr) {
941         /* An invalid reference was encountered; just ignore it. */
942         continue;
943       }
944       grant_role(acl_role, acl_user_to, ref.second);
945       Auth_id_ref authid_role = create_authid_from(acl_role);
946       Auth_id_ref authid_user = create_authid_from(acl_user_to);
947       ret = modify_role_edges_in_table(thd, edge_table, authid_role,
948                                        authid_user, ref.second, false);
949       if (ret) break;
950     }
951   }
952   return ret;
953 }
954 
955 /**
956   Maps a global ACL to a string representation.
957 
958   @param thd Thread handler
959   @param want_access An ACL
960   @param acl_user The associated user which carries the ACL
961   @param [out] global The resulting string
962 
963 */
964 
make_global_privilege_statement(THD * thd,ulong want_access,ACL_USER * acl_user,String * global)965 void make_global_privilege_statement(THD *thd, ulong want_access,
966                                      ACL_USER *acl_user, String *global) {
967   DBUG_ASSERT(assert_acl_cache_read_lock(thd));
968   global->length(0);
969   global->append(STRING_WITH_LEN("GRANT "));
970 
971   if (!(want_access & ~GRANT_ACL))
972     global->append(STRING_WITH_LEN("USAGE"));
973   else {
974     bool found = false;
975     ulong test_access = want_access & ~GRANT_ACL;
976     int counter = 0;
977     ulong j = SELECT_ACL;
978     for (; j <= GLOBAL_ACLS; counter++, j <<= 1) {
979       if (test_access & j) {
980         if (found) global->append(STRING_WITH_LEN(", "));
981         found = true;
982         global->append(global_acls_vector[counter].c_str(),
983                        global_acls_vector[counter].length());
984       }
985     }
986   }
987   global->append(STRING_WITH_LEN(" ON *.* TO "));
988   size_t len = acl_user->get_username_length();
989   append_identifier(thd, global, acl_user->user, len);
990   global->append('@');
991   append_identifier(thd, global, acl_user->host.get_host(),
992                     acl_user->host.get_host_len());
993   if (want_access & GRANT_ACL)
994     global->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
995 }
996 
997 /**
998   Maps a set of database level ACLs to string representations and sends them
999   through the client protocol.
1000 
1001   @param thd The thread handler
1002   @param role The authid associated with the ACLs
1003   @param protocol A handler used for sending data to the client
1004   @param db_map A list of database level ACLs
1005   @param db_wild_map A list of database level ACLs which use pattern matching
1006   @param restrictions List of databases on which there exists different
1007          restrictions for the ACL_USER.
1008 */
1009 
make_database_privilege_statement(THD * thd,ACL_USER * role,Protocol * protocol,const Db_access_map & db_map,const Db_access_map & db_wild_map,const DB_restrictions & restrictions)1010 void make_database_privilege_statement(THD *thd, ACL_USER *role,
1011                                        Protocol *protocol,
1012                                        const Db_access_map &db_map,
1013                                        const Db_access_map &db_wild_map,
1014                                        const DB_restrictions &restrictions) {
1015   DBUG_ASSERT(assert_acl_cache_read_lock(thd));
1016 
1017   auto make_grant_stmts = [thd, role, protocol](const Db_access_map &map) {
1018     for (const Db_access_map::value_type &it : map) {
1019       ulong want_access = it.second;
1020       const std::string &db_name = it.first;
1021 
1022       String db;
1023       db.length(0);
1024       db.append(STRING_WITH_LEN("GRANT "));
1025 
1026       if (test_all_bits(want_access, (DB_OP_ACLS)))
1027         db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
1028       else if (!(want_access & ~GRANT_ACL))
1029         db.append(STRING_WITH_LEN("USAGE"));
1030       else {
1031         int found = 0, cnt;
1032         ulong j, test_access = want_access & ~GRANT_ACL;
1033         for (cnt = 0, j = SELECT_ACL; j <= DB_OP_ACLS; cnt++, j <<= 1) {
1034           if (test_access & j) {
1035             if (found) db.append(STRING_WITH_LEN(", "));
1036             found = 1;
1037             db.append(global_acls_vector[cnt].c_str(),
1038                       global_acls_vector[cnt].length());
1039           }
1040         }
1041       }
1042       db.append(STRING_WITH_LEN(" ON "));
1043       append_identifier(thd, &db, db_name.c_str(), db_name.length());
1044       db.append(STRING_WITH_LEN(".* TO "));
1045       append_identifier(thd, &db, role->user, role->get_username_length());
1046       db.append('@');
1047       // host and lex_user->host are equal except for case
1048       append_identifier(thd, &db, role->host.get_host(),
1049                         role->host.get_host_len());
1050       if (want_access & GRANT_ACL)
1051         db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
1052       protocol->start_row();
1053       protocol->store_string(db.ptr(), db.length(), db.charset());
1054       protocol->end_row();
1055     }
1056   };
1057   auto make_partial_db_revoke_stmts = [thd, protocol,
1058                                        restrictions](ACL_USER *acl_user) {
1059     if (mysqld_partial_revokes()) {
1060       /*
1061        Copy the unordered restrictions into an array.
1062        Send the sorted partial revokes to the client.
1063       */
1064       Mem_root_array<std::pair<std::string, ulong>> restrictions_array(
1065           thd->mem_root);
1066       for (const auto &rl_itr : restrictions.get()) {
1067         restrictions_array.push_back({rl_itr.first, rl_itr.second});
1068       }
1069       std::sort(restrictions_array.begin(), restrictions_array.end(),
1070                 [](const auto &p1, const auto &p2) -> bool {
1071                   return (p1.first.compare(p2.first) <= 0);
1072                 });
1073       for (const auto &rl_itr : restrictions_array) {
1074         String db;
1075         db.length(0);
1076         db.append(STRING_WITH_LEN("REVOKE "));
1077         int found = 0, cnt;
1078         ulong j, test_access = rl_itr.second & ~GRANT_ACL;
1079         for (cnt = 0, j = SELECT_ACL; j <= DB_ACLS; cnt++, j <<= 1) {
1080           if (test_access & j) {
1081             if (found) db.append(STRING_WITH_LEN(", "));
1082             found = 1;
1083             db.append(global_acls_vector[cnt].c_str(),
1084                       global_acls_vector[cnt].length());
1085           }
1086         }
1087 
1088         db.append(STRING_WITH_LEN(" ON "));
1089         append_identifier(thd, &db, rl_itr.first.c_str(),
1090                           rl_itr.first.length());
1091         db.append(STRING_WITH_LEN(".* FROM "));
1092         append_identifier(thd, &db, acl_user->user,
1093                           acl_user->get_username_length());
1094         db.append('@');
1095         // host and lex_user->host are equal except for case
1096         append_identifier(thd, &db, acl_user->host.get_host(),
1097                           acl_user->host.get_host_len());
1098         protocol->start_row();
1099         protocol->store_string(db.ptr(), db.length(), db.charset());
1100         protocol->end_row();
1101       }
1102     }
1103   };
1104 
1105   make_grant_stmts(db_map);
1106   make_grant_stmts(db_wild_map);
1107   make_partial_db_revoke_stmts(role);
1108 }
1109 
1110 /**
1111   Maps a set of global level proxy ACLs to string representations and sends them
1112   through the client protocol.
1113 
1114   @param thd The thread handler
1115   @param user The authid associated with the proxy ACLs.
1116   @param protocol The handler used for sending data through the client protocol
1117 
1118 */
1119 
make_proxy_privilege_statement(THD * thd MY_ATTRIBUTE ((unused)),ACL_USER * user,Protocol * protocol)1120 void make_proxy_privilege_statement(THD *thd MY_ATTRIBUTE((unused)),
1121                                     ACL_USER *user, Protocol *protocol) {
1122   DBUG_ASSERT(assert_acl_cache_read_lock(thd));
1123   for (ACL_PROXY_USER *proxy = acl_proxy_users->begin();
1124        proxy != acl_proxy_users->end(); ++proxy) {
1125     if (proxy->granted_on(user->host.get_host(), user->user)) {
1126       String global;
1127       proxy->print_grant(&global);
1128       protocol->start_row();
1129       protocol->store_string(global.ptr(), global.length(), global.charset());
1130       protocol->end_row();
1131     }
1132   }
1133 }
1134 
1135 /**
1136   Maps a set of database level ACLs for stored programs to string
1137   representations and sends them through the client protocol.
1138 
1139   @param thd A thread handler
1140   @param role The authid associated with the ACLs
1141   @param protocol The handler used for sending data through the client protocol
1142   @param sp_map The ACLs granted to role
1143   @param type Either 0 for procedures or 1 for functions
1144 
1145 */
1146 
make_sp_privilege_statement(THD * thd,ACL_USER * role,Protocol * protocol,SP_access_map & sp_map,int type)1147 void make_sp_privilege_statement(THD *thd, ACL_USER *role, Protocol *protocol,
1148                                  SP_access_map &sp_map, int type) {
1149   DBUG_ASSERT(assert_acl_cache_read_lock(thd));
1150   SP_access_map::iterator it = sp_map.begin();
1151   for (; it != sp_map.end(); ++it) {
1152     ulong want_access = it->second;
1153     std::string sp_name = it->first;
1154 
1155     String db;
1156     db.length(0);
1157     db.append(STRING_WITH_LEN("GRANT "));
1158 
1159     if (test_all_bits(want_access, (DB_OP_ACLS)))
1160       db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
1161     else if (!(want_access & ~GRANT_ACL))
1162       db.append(STRING_WITH_LEN("USAGE"));
1163     else {
1164       int found = 0, cnt;
1165       ulong j, test_access = want_access & ~GRANT_ACL;
1166       for (cnt = 0, j = SELECT_ACL; j <= DB_OP_ACLS; cnt++, j <<= 1) {
1167         if (test_access & j) {
1168           if (found) db.append(STRING_WITH_LEN(", "));
1169           found = 1;
1170           db.append(global_acls_vector[cnt].c_str(),
1171                     global_acls_vector[cnt].length());
1172         }
1173       }
1174     }
1175     db.append(STRING_WITH_LEN(" ON "));
1176     if (type == 0)
1177       db.append(STRING_WITH_LEN("PROCEDURE "));
1178     else
1179       db.append(STRING_WITH_LEN("FUNCTION "));
1180     db.append(sp_name.c_str(), sp_name.length());
1181     db.append(STRING_WITH_LEN(" TO "));
1182     append_identifier(thd, &db, role->user, role->get_username_length());
1183     db.append(STRING_WITH_LEN("@"));
1184     // host and lex_user->host are equal except for case
1185     append_identifier(thd, &db, role->host.get_host(),
1186                       role->host.get_host_len());
1187     if (want_access & GRANT_ACL)
1188       db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
1189     protocol->start_row();
1190     protocol->store_string(db.ptr(), db.length(), db.charset());
1191     protocol->end_row();
1192   }
1193 }
1194 
make_with_admin_privilege_statement(THD * thd,ACL_USER * acl_user,Protocol * protocol,const Grant_acl_set & with_admin_acl,const List_of_granted_roles & granted_roles)1195 void make_with_admin_privilege_statement(
1196     THD *thd, ACL_USER *acl_user, Protocol *protocol,
1197     const Grant_acl_set &with_admin_acl,
1198     const List_of_granted_roles &granted_roles) {
1199   DBUG_ASSERT(assert_acl_cache_read_lock(thd));
1200   if (granted_roles.size() == 0) return;
1201   std::set<std::string> sorted_copy_of_granted_role_str;
1202   for (auto &rid : granted_roles) {
1203     if (!rid.second) continue;  // this is not granted WITH ADMIN
1204     std::string key;
1205     rid.first.auth_str(&key);
1206     sorted_copy_of_granted_role_str.insert(key);
1207   }
1208   for (auto &s : with_admin_acl) {
1209     sorted_copy_of_granted_role_str.insert(s);
1210   }
1211 
1212   if (sorted_copy_of_granted_role_str.size() == 0) return;
1213   std::set<std::string>::iterator it = sorted_copy_of_granted_role_str.begin();
1214   String global;
1215   global.append(STRING_WITH_LEN("GRANT "));
1216   bool found = false;
1217   for (; it != sorted_copy_of_granted_role_str.end(); ++it) {
1218     if (it != sorted_copy_of_granted_role_str.begin()) global.append(',');
1219     global.append(it->c_str(), it->length());
1220     found = true;
1221   }
1222   if (found) {
1223     global.append(STRING_WITH_LEN(" TO "));
1224     append_identifier(thd, &global, acl_user->user,
1225                       acl_user->get_username_length());
1226     global.append('@');
1227     append_identifier(thd, &global, acl_user->host.get_host(),
1228                       acl_user->host.get_host_len());
1229     global.append(" WITH ADMIN OPTION");
1230 
1231     protocol->start_row();
1232     protocol->store_string(global.ptr(), global.length(), global.charset());
1233     protocol->end_row();
1234   }
1235 }
1236 
make_dynamic_privilege_statement(THD * thd,ACL_USER * role,Protocol * protocol,const Dynamic_privileges & dyn_priv)1237 void make_dynamic_privilege_statement(THD *thd, ACL_USER *role,
1238                                       Protocol *protocol,
1239                                       const Dynamic_privileges &dyn_priv) {
1240   DBUG_ASSERT(assert_acl_cache_read_lock(thd));
1241   bool found = false;
1242   /*
1243     On first iteration create a statement out of all the grants which don't
1244     have a grant option.
1245     On second iteration process all privileges with a grant option.
1246   */
1247   for (int grant_option = 0; grant_option < 2; ++grant_option) {
1248     String global;
1249     global.append(STRING_WITH_LEN("GRANT "));
1250     for (auto &&priv : dyn_priv) {
1251       if (grant_option == 0 && priv.second) continue;
1252       if (grant_option == 1 && !priv.second) continue;
1253       if (found) global.append(',');
1254       global.append(priv.first.c_str(), priv.first.length());
1255       found = true;
1256     }
1257     if (found) {
1258       /* Dynamic privileges are always applied on global level */
1259       global.append(STRING_WITH_LEN(" ON *.* TO "));
1260       if (role->user != nullptr)
1261         append_identifier(thd, &global, role->user,
1262                           role->get_username_length());
1263       else
1264         global.append(STRING_WITH_LEN("''"));
1265       global.append('@');
1266       append_identifier(thd, &global, role->host.get_host(),
1267                         role->host.get_host_len());
1268       if (grant_option) global.append(" WITH GRANT OPTION");
1269       protocol->start_row();
1270       protocol->store_string(global.ptr(), global.length(), global.charset());
1271       protocol->end_row();
1272     }
1273     found = false;
1274   }  // end for
1275 }
1276 
make_roles_privilege_statement(THD * thd,ACL_USER * role,Protocol * protocol,List_of_granted_roles & granted_roles,bool show_mandatory_roles)1277 void make_roles_privilege_statement(THD *thd, ACL_USER *role,
1278                                     Protocol *protocol,
1279                                     List_of_granted_roles &granted_roles,
1280                                     bool show_mandatory_roles) {
1281   DBUG_ASSERT(assert_acl_cache_read_lock(thd));
1282   String global;
1283   global.append(STRING_WITH_LEN("GRANT "));
1284 
1285   bool found = false;
1286   std::vector<Role_id> mandatory_roles;
1287   /*
1288     Because the output of SHOW GRANTS is used by tools like mysqldump we
1289     cannot include mandatory roles if the FOR clause is used.
1290   */
1291   if (show_mandatory_roles) {
1292     get_mandatory_roles(&mandatory_roles);
1293   }
1294   if (granted_roles.size() == 0 && mandatory_roles.size() == 0) return;
1295   /* First list granted roles which doesn't have WITH ADMIN */
1296   std::sort(granted_roles.begin(), granted_roles.end());
1297   List_of_granted_roles::iterator it = granted_roles.begin();
1298   std::vector<Role_id>::iterator it2 = mandatory_roles.begin();
1299   bool got_more_mandatory_roles = (it2 != mandatory_roles.end());
1300   bool got_more_granted_roles = (it != granted_roles.end());
1301   while (got_more_mandatory_roles || got_more_granted_roles) {
1302     if (got_more_granted_roles && !it->second &&
1303         !(got_more_mandatory_roles && *it2 < it->first)) {
1304       if (found) global.append(',');
1305       append_identifier(thd, &global, it->first.user().c_str(),
1306                         it->first.user().length());
1307       global.append('@');
1308       append_identifier(thd, &global, it->first.host().c_str(),
1309                         it->first.host().length());
1310       found = true;
1311       if (got_more_mandatory_roles && it->first == *it2) ++it2;
1312       ++it;
1313     } else if (got_more_mandatory_roles) {
1314       if (found) global.append(',');
1315       append_identifier(thd, &global, it2->user().c_str(),
1316                         it2->user().length());
1317       global.append('@');
1318       append_identifier(thd, &global, it2->host().c_str(),
1319                         it2->host().length());
1320       found = true;
1321       ++it2;
1322     } else
1323       ++it;
1324     got_more_mandatory_roles = (it2 != mandatory_roles.end());
1325     got_more_granted_roles = (it != granted_roles.end());
1326   }  // end while
1327   if (found) {
1328     global.append(STRING_WITH_LEN(" TO "));
1329     append_identifier(thd, &global, role->user, role->get_username_length());
1330     global.append('@');
1331     append_identifier(thd, &global, role->host.get_host(),
1332                       role->host.get_host_len());
1333     protocol->start_row();
1334     protocol->store_string(global.ptr(), global.length(), global.charset());
1335     protocol->end_row();
1336   }
1337 }
1338 
make_table_privilege_statement(THD * thd,ACL_USER * role,Protocol * protocol,Table_access_map & table_map)1339 void make_table_privilege_statement(THD *thd, ACL_USER *role,
1340                                     Protocol *protocol,
1341                                     Table_access_map &table_map) {
1342   DBUG_ASSERT(assert_acl_cache_read_lock(thd));
1343   Table_access_map::iterator it = table_map.begin();
1344   for (; it != table_map.end(); ++it) {
1345     std::string qualified_table_name = it->first;
1346     Grant_table_aggregate agg = it->second;
1347     String global;
1348     ulong test_access = (agg.table_access | agg.cols) & ~GRANT_ACL;
1349 
1350     global.length(0);
1351     global.append(STRING_WITH_LEN("GRANT "));
1352 
1353     if (test_all_bits(agg.table_access, (TABLE_OP_ACLS)))
1354       global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
1355     else if (!test_access)
1356       global.append(STRING_WITH_LEN("USAGE"));
1357     else {
1358       /* Add specific column access */
1359       int found = 0;
1360       ulong j;
1361       ulong counter;
1362       for (counter = 0, j = SELECT_ACL; j <= TABLE_OP_ACLS;
1363            counter++, j <<= 1) {
1364         if (test_access & j) {
1365           if (found) global.append(STRING_WITH_LEN(", "));
1366           found = 1;
1367           global.append(global_acls_vector[counter].c_str(),
1368                         global_acls_vector[counter].length());
1369 
1370           if (agg.cols) {
1371             uint found_col = 0;
1372             Column_map::iterator col_it = agg.columns.begin();
1373             for (; col_it != agg.columns.end(); ++col_it) {
1374               if (col_it->second & j) {
1375                 if (!found_col) {
1376                   found_col = 1;
1377                   /*
1378                     If we have a duplicated table level privilege, we
1379                     must write the access privilege name again.
1380                   */
1381                   if (agg.table_access & j) {
1382                     global.append(STRING_WITH_LEN(", "));
1383                     global.append(global_acls_vector[counter].c_str(),
1384                                   global_acls_vector[counter].length());
1385                   }
1386                   global.append(STRING_WITH_LEN(" ("));
1387                 } else
1388                   global.append(STRING_WITH_LEN(", "));
1389                 append_identifier(thd, &global, col_it->first.c_str(),
1390                                   col_it->first.length());
1391               }
1392             }
1393             if (found_col) global.append(')');
1394           }
1395         }
1396       }
1397     }
1398     global.append(STRING_WITH_LEN(" ON "));
1399     global.append(qualified_table_name.c_str(), qualified_table_name.length());
1400     global.append(STRING_WITH_LEN(" TO "));
1401     append_identifier(thd, &global, role->user, role->get_username_length());
1402     global.append('@');
1403     // host and lex_user->host are equal except for case
1404     append_identifier(thd, &global, role->host.get_host(),
1405                       role->host.get_host_len());
1406     if (agg.table_access & GRANT_ACL)
1407       global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
1408     protocol->start_row();
1409     protocol->store_string(global.ptr(), global.length(), global.charset());
1410     protocol->end_row();
1411   }
1412 }
1413 
get_sp_access_map(ACL_USER * acl_user,SP_access_map * sp_map,malloc_unordered_multimap<std::string,unique_ptr_destroy_only<GRANT_NAME>> * hash)1414 void get_sp_access_map(
1415     ACL_USER *acl_user, SP_access_map *sp_map,
1416     malloc_unordered_multimap<std::string, unique_ptr_destroy_only<GRANT_NAME>>
1417         *hash) {
1418   DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
1419   /* Add routine access */
1420   for (const auto &key_and_value : *hash) {
1421     GRANT_NAME *grant_proc = key_and_value.second.get();
1422     const char *user, *host;
1423     if (!(user = grant_proc->user)) user = "";
1424     if (!(host = grant_proc->host.get_host())) host = "";
1425     const char *acl_user_host, *acl_user_user;
1426     if (!(acl_user_host = acl_user->host.get_host())) acl_user_host = "";
1427     if (!(acl_user_user = acl_user->user)) acl_user_user = "";
1428 
1429     /*
1430       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
1431       but make it case-insensitive because that's the way they are
1432       actually applied, and showing fewer privileges than are applied
1433       would be wrong from a security point of view.
1434     */
1435 
1436     if (!strcmp(acl_user_user, user) &&
1437         !my_strcasecmp(system_charset_info, acl_user_host, host)) {
1438       ulong proc_access = grant_proc->privs;
1439       if (proc_access != 0) {
1440         String key;
1441         append_identifier(&key, grant_proc->db, strlen(grant_proc->db));
1442         key.append(".");
1443         append_identifier(&key, grant_proc->tname, strlen(grant_proc->tname));
1444         (*sp_map)[std::string(key.c_ptr())] |= proc_access;
1445       }
1446     }
1447   }
1448 }
1449 
get_table_access_map(ACL_USER * acl_user,Table_access_map * table_map)1450 void get_table_access_map(ACL_USER *acl_user, Table_access_map *table_map) {
1451   DBUG_TRACE;
1452   DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
1453   for (const auto &key_and_value : *column_priv_hash) {
1454     GRANT_TABLE *grant_table = key_and_value.second.get();
1455     const char *user, *host;
1456 
1457     if (!(user = grant_table->user)) user = "";
1458     if (!(host = grant_table->host.get_host())) host = "";
1459     const char *acl_user_host, *acl_user_user;
1460     if (!(acl_user_host = acl_user->host.get_host())) acl_user_host = "";
1461     if (!(acl_user_user = acl_user->user)) acl_user_user = "";
1462 
1463     /*
1464       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
1465       but make it case-insensitive because that's the way they are
1466       actually applied, and showing fewer privileges than are applied
1467       would be wrong from a security point of view.
1468     */
1469     if (!strcmp(acl_user_user, user) &&
1470         !my_strcasecmp(system_charset_info, acl_user_host, host)) {
1471       ulong table_access = grant_table->privs;
1472       if ((table_access | grant_table->cols) != 0) {
1473         String q_name;
1474         const THD *thd = table_map->get_thd();
1475         append_identifier(thd, &q_name, grant_table->db,
1476                           strlen(grant_table->db));
1477         q_name.append(".");
1478         append_identifier(thd, &q_name, grant_table->tname,
1479                           strlen(grant_table->tname));
1480         Grant_table_aggregate agg = (*table_map)[std::string(q_name.c_ptr())];
1481         // cols is an ACL of all privileges found as column privileges in the
1482         // table given any column in that table. Before a hash look up
1483         // you can check in this column if the column exist in the first place
1484         // for the required privilege
1485         agg.cols |= grant_table->cols;
1486         agg.table_access |= grant_table->privs;
1487         if (grant_table->cols) {
1488           DBUG_PRINT("info", ("Collecting column privileges for %s@%s",
1489                               acl_user->user, acl_user->host.get_host()));
1490           // Iterate over all column ACLs for this table.
1491           for (const auto &key_and_value_acl : grant_table->hash_columns) {
1492             String q_col_name;
1493             GRANT_COLUMN *col = key_and_value_acl.second.get();
1494             // TODO why can this be 0x0 ?!
1495             if (col) {
1496               std::string str_column_name(col->column);
1497               ulong col_access = agg.columns[str_column_name];
1498               col_access |= col->rights;
1499               agg.columns[str_column_name] = col_access;
1500               DBUG_PRINT("info", ("Found privilege %lu on %s.%s", col_access,
1501                                   q_name.c_ptr(), q_col_name.c_ptr()));
1502             }
1503           }
1504         }
1505         (*table_map)[std::string(q_name.c_ptr())] = agg;
1506       }
1507     }
1508   }  // end for
1509 }
1510 
get_dynamic_privileges(ACL_USER * acl_user,Dynamic_privileges * acl)1511 void get_dynamic_privileges(ACL_USER *acl_user, Dynamic_privileges *acl) {
1512   Role_id key(create_authid_from(acl_user));
1513   User_to_dynamic_privileges_map::iterator it, it_end;
1514 
1515   std::tie(it, it_end) = g_dynamic_privileges_map->equal_range(key);
1516   for (; it != it_end; ++it) {
1517     auto aggr = acl->find(it->second.first);
1518     if (aggr != acl->end() && aggr->second != it->second.second) {
1519       /*
1520         If this privID was already in the aggregate we make sure that the
1521         grant option take precedence; any GRANT OPTION will be sticky through
1522         out role privilege aggregation.
1523       */
1524       aggr->second = true;
1525     } else
1526       acl->insert(it->second);
1527   }
1528 }
1529 
has_wildcard_characters(const LEX_CSTRING & db)1530 bool has_wildcard_characters(const LEX_CSTRING &db) {
1531   return (memchr(db.str, wild_one, db.length) != nullptr ||
1532           memchr(db.str, wild_many, db.length) != nullptr);
1533 }
1534 
get_database_access_map(ACL_USER * acl_user,Db_access_map * db_map,Db_access_map * db_wild_map)1535 void get_database_access_map(ACL_USER *acl_user, Db_access_map *db_map,
1536                              Db_access_map *db_wild_map) {
1537   ACL_DB *acl_db;
1538   DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
1539   for (acl_db = acl_dbs->begin(); acl_db != acl_dbs->end(); ++acl_db) {
1540     const char *acl_db_user, *acl_db_host;
1541     if (!(acl_db_user = acl_db->user)) acl_db_user = "";
1542     if (!(acl_db_host = acl_db->host.get_host())) acl_db_host = "";
1543     const char *acl_user_host, *acl_user_user;
1544     if (!(acl_user_host = acl_user->host.get_host())) acl_user_host = "";
1545     if (!(acl_user_user = acl_user->user)) acl_user_user = "";
1546 
1547     /*
1548       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
1549       but make it case-insensitive because that's the way they are
1550       actually applied, and showing fewer privileges than are applied
1551       would be wrong from a security point of view.
1552     */
1553 
1554     if (!strcmp(acl_user_user, acl_db_user) &&
1555         !my_strcasecmp(system_charset_info, acl_user_host, acl_db_host)) {
1556       ulong want_access = acl_db->access;
1557       if (want_access) {
1558         if (has_wildcard_characters({acl_db->db, strlen(acl_db->db)})) {
1559           (*db_wild_map)[std::string(acl_db->db)] |= want_access;
1560         } else {
1561           (*db_map)[std::string(acl_db->db)] |= want_access;
1562         }
1563         DBUG_PRINT("info", ("Role: %s db: %s acl: %lu", acl_user_user,
1564                             acl_db->db, want_access));
1565       }  // end if access
1566     }
1567   }  // end for
1568 }
1569 
1570 /**
1571   A graph visitor used for doing breadth-first traversal of the global role
1572   graph. The visitor takes a set of access maps and aggregate all discovered
1573   privileges into these maps.
1574 */
1575 class Get_access_maps : public boost::default_bfs_visitor {
1576  public:
Get_access_maps(ACL_USER * acl_user,ulong * access,Db_access_map * db_map,Db_access_map * db_wild_map,Table_access_map * table_map,SP_access_map * sp_map,SP_access_map * func_map,Grant_acl_set * with_admin_acl,Dynamic_privileges * dyn_acl,Restrictions * restrictions)1577   Get_access_maps(ACL_USER *acl_user, ulong *access, Db_access_map *db_map,
1578                   Db_access_map *db_wild_map, Table_access_map *table_map,
1579                   SP_access_map *sp_map, SP_access_map *func_map,
1580                   Grant_acl_set *with_admin_acl, Dynamic_privileges *dyn_acl,
1581                   Restrictions *restrictions)
1582       : m_access(access),
1583         m_db_map(db_map),
1584         m_db_wild_map(db_wild_map),
1585         m_table_map(table_map),
1586         m_sp_map(sp_map),
1587         m_func_map(func_map),
1588         m_with_admin_acl(with_admin_acl),
1589         m_dynamic_acl(dyn_acl),
1590         m_restrictions(restrictions),
1591         m_grantee{acl_user->user, acl_user->get_username_length(),
1592                   acl_user->host.get_host(), acl_user->host.get_host_len()} {}
1593   template <typename Vertex, typename Graph>
discover_vertex(Vertex u,const Graph &) const1594   void discover_vertex(Vertex u, const Graph &) const {
1595     ACL_USER acl_user = get(boost::vertex_acl_user_t(), *g_granted_roles)[u];
1596     if (acl_user.user == g_active_dummy_user) return;  // skip root node
1597     DBUG_PRINT("info",
1598                ("Role visitor in %s@%s, adding global access %lu\n",
1599                 acl_user.user, acl_user.host.get_host(), acl_user.access));
1600     /* Add database access */
1601     get_database_access_map(&acl_user, m_db_map, m_db_wild_map);
1602 
1603     /* Add restrictions */
1604     {
1605       /* DB Restrictions */
1606       const Auth_id granter(&acl_user);
1607       Restrictions restrictions =
1608           acl_restrictions->find_restrictions(&acl_user);
1609       std::unique_ptr<Restrictions_aggregator> aggregator =
1610           Restrictions_aggregator_factory::create(
1611               granter, m_grantee, acl_user.access & DB_ACLS,
1612               *m_access & DB_ACLS, restrictions.db(), m_restrictions->db(),
1613               acl_user.access & DB_ACLS, m_db_map);
1614       if (aggregator) {
1615         DB_restrictions db_restrictions(nullptr);
1616         if (aggregator->generate(db_restrictions)) return;
1617         m_restrictions->set_db(db_restrictions);
1618       }
1619     }
1620     /* Add global access */
1621     /*
1622       Up-cast to base class to avoid gcc 7.1.1 warning:
1623       dereferencing type-punned pointer will break strict-aliasing rules
1624      */
1625     *m_access |= implicit_cast<ACL_ACCESS *>(&acl_user)->access;
1626 
1627     /* Add table access */
1628     get_table_access_map(&acl_user, m_table_map);
1629 
1630     /* Add stored procedure access */
1631     get_sp_access_map(&acl_user, m_sp_map, proc_priv_hash.get());
1632 
1633     /* Add user function access */
1634     get_sp_access_map(&acl_user, m_func_map, func_priv_hash.get());
1635 
1636     /* Add dynamic privileges */
1637     get_dynamic_privileges(&acl_user, m_dynamic_acl);
1638   }
1639 
1640   template <typename Edge, typename Graph>
examine_edge(const Edge & edge,Graph & granted_roles)1641   void examine_edge(const Edge &edge, Graph &granted_roles) {
1642     ACL_USER to_user =
1643         boost::get(boost::vertex_acl_user_t(),
1644                    granted_roles)[boost::target(edge, granted_roles)];
1645     int with_admin_opt =
1646         boost::get(boost::edge_capacity_t(), granted_roles)[edge];
1647     if (with_admin_opt) {
1648       String qname;
1649       append_identifier(&qname, to_user.user, strlen(to_user.user));
1650       qname.append('@');
1651       /* Up-cast to base class, see above. */
1652       append_identifier(
1653           &qname, implicit_cast<ACL_ACCESS *>(&to_user)->host.get_host(),
1654           implicit_cast<ACL_ACCESS *>(&to_user)->host.get_host_len());
1655       /* We save the granted role in the Acl_map of the granted user */
1656       m_with_admin_acl->insert(
1657           std::string(qname.c_ptr_quick(), qname.length()));
1658     }
1659   }
1660 
1661  private:
1662   ulong *m_access;
1663   Db_access_map *m_db_map;
1664   Db_access_map *m_db_wild_map;
1665   Table_access_map *m_table_map;
1666   SP_access_map *m_sp_map;
1667   SP_access_map *m_func_map;
1668   Grant_acl_set *m_with_admin_acl;
1669   Dynamic_privileges *m_dynamic_acl;
1670   Restrictions *m_restrictions;
1671   Auth_id m_grantee;
1672 };
1673 
1674 /**
1675   Get a cached internal schema access.
1676   @param grant_internal_info the cache
1677   @param schema_name the name of the internal schema
1678 */
get_cached_schema_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name)1679 const ACL_internal_schema_access *get_cached_schema_access(
1680     GRANT_INTERNAL_INFO *grant_internal_info, const char *schema_name) {
1681   if (grant_internal_info) {
1682     if (!grant_internal_info->m_schema_lookup_done) {
1683       grant_internal_info->m_schema_access =
1684           ACL_internal_schema_registry::lookup(schema_name);
1685       grant_internal_info->m_schema_lookup_done = true;
1686     }
1687     return grant_internal_info->m_schema_access;
1688   }
1689   return ACL_internal_schema_registry::lookup(schema_name);
1690 }
1691 
1692 /**
1693   Get a cached internal table access.
1694   @param grant_internal_info the cache
1695   @param schema_name the name of the internal schema
1696   @param table_name the name of the internal table
1697 */
get_cached_table_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name,const char * table_name)1698 const ACL_internal_table_access *get_cached_table_access(
1699     GRANT_INTERNAL_INFO *grant_internal_info, const char *schema_name,
1700     const char *table_name) {
1701   DBUG_ASSERT(grant_internal_info);
1702   if (!grant_internal_info->m_table_lookup_done) {
1703     const ACL_internal_schema_access *schema_access;
1704     schema_access = get_cached_schema_access(grant_internal_info, schema_name);
1705     if (schema_access)
1706       grant_internal_info->m_table_access = schema_access->lookup(table_name);
1707     grant_internal_info->m_table_lookup_done = true;
1708   }
1709   return grant_internal_info->m_table_access;
1710 }
1711 
check(ulong want_access,ulong * save_priv) const1712 ACL_internal_access_result IS_internal_schema_access::check(
1713     ulong want_access, ulong *save_priv) const {
1714   want_access &= ~SELECT_ACL;
1715 
1716   /*
1717     We don't allow any simple privileges but SELECT_ACL on
1718     the information_schema database.
1719   */
1720   if (unlikely(want_access & DB_ACLS)) return ACL_INTERNAL_ACCESS_DENIED;
1721 
1722   /* Always grant SELECT for the information schema. */
1723   *save_priv |= SELECT_ACL;
1724 
1725   return want_access ? ACL_INTERNAL_ACCESS_CHECK_GRANT
1726                      : ACL_INTERNAL_ACCESS_GRANTED;
1727 }
1728 
lookup(const char *) const1729 const ACL_internal_table_access *IS_internal_schema_access::lookup(
1730     const char *) const {
1731   /* There are no per table rules for the information schema. */
1732   return nullptr;
1733 }
1734 
1735 /**
1736   Check privileges for LOCK TABLES statement.
1737 
1738   @param thd     Thread context.
1739   @param tables  List of tables to be locked.
1740 
1741   @retval false - Success.
1742   @retval true  - Failure.
1743 */
1744 
lock_tables_precheck(THD * thd,TABLE_LIST * tables)1745 bool lock_tables_precheck(THD *thd, TABLE_LIST *tables) {
1746   TABLE_LIST *first_not_own_table = thd->lex->first_not_own_table();
1747 
1748   for (TABLE_LIST *table = tables; table != first_not_own_table && table;
1749        table = table->next_global) {
1750     if (is_temporary_table(table)) continue;
1751 
1752     if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, table, false, 1,
1753                            false))
1754       return true;
1755   }
1756 
1757   return false;
1758 }
1759 
1760 /**
1761   CREATE TABLE query pre-check.
1762 
1763   @param thd			Thread handler
1764   @param tables		Global table list
1765   @param create_table	        Table which will be created
1766 
1767   @retval
1768     false   OK
1769   @retval
1770     true   Error
1771 */
1772 
create_table_precheck(THD * thd,TABLE_LIST * tables,TABLE_LIST * create_table)1773 bool create_table_precheck(THD *thd, TABLE_LIST *tables,
1774                            TABLE_LIST *create_table) {
1775   LEX *lex = thd->lex;
1776   SELECT_LEX *select_lex = lex->select_lex;
1777   ulong want_priv;
1778   bool error = true;  // Error message is given
1779   DBUG_TRACE;
1780 
1781   /*
1782     Require CREATE [TEMPORARY] privilege on new table; for
1783     CREATE TABLE ... SELECT, also require INSERT.
1784   */
1785 
1786   want_priv =
1787       (lex->create_info->options & HA_LEX_CREATE_TMP_TABLE)
1788           ? CREATE_TMP_ACL
1789           : (CREATE_ACL | (select_lex->fields_list.elements ? INSERT_ACL : 0));
1790 
1791   if (check_access(thd, want_priv, create_table->db,
1792                    &create_table->grant.privilege,
1793                    &create_table->grant.m_internal, false, false))
1794     goto err;
1795 
1796   /* If it is a merge table, check privileges for merge children. */
1797   if (lex->create_info->merge_list.first) {
1798     /*
1799       The user must have (SELECT_ACL | UPDATE_ACL | DELETE_ACL) on the
1800       underlying base tables, even if there are temporary tables with the same
1801       names.
1802 
1803       From user's point of view, it might look as if the user must have these
1804       privileges on temporary tables to create a merge table over them. This is
1805       one of two cases when a set of privileges is required for operations on
1806       temporary tables (see also CREATE TABLE).
1807 
1808       The reason for this behavior stems from the following facts:
1809 
1810         - For merge tables, the underlying table privileges are checked only
1811           at CREATE TABLE / ALTER TABLE time.
1812 
1813           In other words, once a merge table is created, the privileges of
1814           the underlying tables can be revoked, but the user will still have
1815           access to the merge table (provided that the user has privileges on
1816           the merge table itself).
1817 
1818         - Temporary tables shadow base tables.
1819 
1820           I.e. there might be temporary and base tables with the same name, and
1821           the temporary table takes the precedence in all operations.
1822 
1823         - For temporary MERGE tables we do not track if their child tables are
1824           base or temporary. As result we can't guarantee that privilege check
1825           which was done in presence of temporary child will stay relevant later
1826           as this temporary table might be removed.
1827 
1828       If SELECT_ACL | UPDATE_ACL | DELETE_ACL privileges were not checked for
1829       the underlying *base* tables, it would create a security breach as in
1830       Bug#12771903.
1831     */
1832 
1833     if (check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
1834                            lex->create_info->merge_list.first, false, UINT_MAX,
1835                            false))
1836       goto err;
1837   }
1838 
1839   if (want_priv != CREATE_TMP_ACL &&
1840       check_grant(thd, want_priv, create_table, false, 1, false))
1841     goto err;
1842 
1843   if (select_lex->fields_list.elements) {
1844     /* Check permissions for used tables in CREATE TABLE ... SELECT */
1845     if (tables &&
1846         check_table_access(thd, SELECT_ACL, tables, false, UINT_MAX, false))
1847       goto err;
1848   } else if (lex->create_info->options & HA_LEX_CREATE_TABLE_LIKE) {
1849     if (check_table_access(thd, SELECT_ACL, tables, false, UINT_MAX, false))
1850       goto err;
1851   }
1852 
1853   if (check_fk_parent_table_access(thd, lex->create_info, lex->alter_info))
1854     goto err;
1855 
1856   error = false;
1857 
1858 err:
1859   return error;
1860 }
1861 
1862 /**
1863   @brief Performs standardized check whether to prohibit (true)
1864     or allow (false) operations based on read_only and super_read_only
1865     state.
1866   @param thd              Thread handler
1867   @param err_if_readonly  Boolean indicating whether or not
1868     to add the error to the thread context if read-only is
1869     violated.
1870 
1871   @returns Status code
1872     @retval true The operation should be prohibited.
1873 @   retval false The operation should be allowed.
1874 */
check_readonly(THD * thd,bool err_if_readonly)1875 bool check_readonly(THD *thd, bool err_if_readonly) {
1876   DBUG_TRACE;
1877 
1878   /* read_only=OFF, do not prohibit operation: */
1879   if (!opt_readonly) return false;
1880 
1881   /*
1882     Thread is replication slave or skip_read_only check is enabled for the
1883     command, do not prohibit operation.
1884   */
1885   if (thd->slave_thread || thd->is_cmd_skip_readonly()) return false;
1886 
1887   Security_context *sctx = thd->security_context();
1888   bool is_super =
1889       sctx->check_access(SUPER_ACL) ||
1890       sctx->has_global_grant(STRING_WITH_LEN("CONNECTION_ADMIN")).first;
1891 
1892   /* super_read_only=OFF and user has SUPER privilege,
1893   do not prohibit operation:
1894   */
1895   if (is_super && !opt_super_readonly) return false;
1896 
1897   /* throw error in standardized way if requested: */
1898   if (err_if_readonly) err_readonly(thd);
1899 
1900   /* in all other cases, prohibit operation: */
1901   return true;
1902 }
1903 
1904 /**
1905   @brief Generates appropriate error messages for read-only state
1906     depending on whether user has SUPER privilege or not.
1907 
1908   @param thd              Thread handler
1909 
1910 */
err_readonly(THD * thd)1911 void err_readonly(THD *thd) {
1912   my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
1913            thd->security_context()->check_access(SUPER_ACL) ||
1914                    thd->security_context()
1915                        ->has_global_grant(STRING_WITH_LEN("CONNECTION_ADMIN"))
1916                        .first
1917                ? "--super-read-only"
1918                : "--read-only");
1919 }
1920 
1921 /**
1922   Check grants for commands which work only with one table and all other
1923   tables belonging to subselects or implicitly opened tables.
1924 
1925   @param thd			Thread handler
1926   @param privilege		requested privilege
1927   @param all_tables		global table list of query
1928 
1929   @returns false on success, true on access denied error
1930 */
1931 
check_one_table_access(THD * thd,ulong privilege,TABLE_LIST * all_tables)1932 bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) {
1933   if (check_single_table_access(thd, privilege, all_tables, false)) return true;
1934 
1935   // Check privileges on tables from subqueries and implicitly opened tables
1936   TABLE_LIST *subquery_table;
1937   TABLE_LIST *const view = all_tables->is_view() ? all_tables : nullptr;
1938 
1939   if ((subquery_table = all_tables->next_global)) {
1940     /*
1941       Access rights asked for the first table of a view should be the same
1942       as for the view
1943     */
1944     if (view && subquery_table->belong_to_view == view) {
1945       if (check_single_table_access(thd, privilege, subquery_table, false))
1946         return true; /* purecov: inspected */
1947       subquery_table = subquery_table->next_global;
1948     }
1949     if (subquery_table && check_table_access(thd, SELECT_ACL, subquery_table,
1950                                              false, UINT_MAX, false))
1951       return true;
1952   }
1953   return false;
1954 }
1955 
1956 /**
1957   Check grants for commands which work only with one table.
1958 
1959   @param thd                    Thread handler
1960   @param privilege              requested privilege
1961   @param all_tables             global table list of query
1962   @param no_errors              false/true - report/don't report error to
1963                             the client (using my_error() call).
1964 
1965   @retval
1966     0   OK
1967   @retval
1968     1   access denied, error is sent to client
1969 */
1970 
check_single_table_access(THD * thd,ulong privilege,TABLE_LIST * all_tables,bool no_errors)1971 bool check_single_table_access(THD *thd, ulong privilege,
1972                                TABLE_LIST *all_tables, bool no_errors) {
1973   /*
1974     Optimizer internal tables and the DD tables used under the
1975     INFORMATION_SCHEMA system views does not need any privilege checking.
1976   */
1977   if (all_tables->is_internal() ||
1978       (all_tables->referencing_view &&
1979        all_tables->referencing_view->is_system_view)) {
1980     all_tables->set_privileges(privilege);
1981     return false;
1982   }
1983 
1984   Security_context *backup_ctx = thd->security_context();
1985 
1986   /* we need to switch to the saved context (if any) */
1987   if (all_tables->security_ctx)
1988     thd->set_security_context(all_tables->security_ctx);
1989 
1990   const char *db_name;
1991   if ((all_tables->is_view() || all_tables->field_translation) &&
1992       !all_tables->schema_table)
1993     db_name = all_tables->view_db.str;
1994   else
1995     db_name = all_tables->db;
1996 
1997   if (check_access(thd, privilege, db_name, &all_tables->grant.privilege,
1998                    &all_tables->grant.m_internal, false, no_errors))
1999     goto deny;
2000 
2001   /* Show only 1 table for check_grant */
2002   if (!(all_tables->belong_to_view &&
2003         (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
2004       check_grant(thd, privilege, all_tables, false, 1, no_errors))
2005     goto deny;
2006 
2007   thd->set_security_context(backup_ctx);
2008   return false;
2009 
2010 deny:
2011   thd->set_security_context(backup_ctx);
2012   return true;
2013 }
2014 
check_routine_access(THD * thd,ulong want_access,const char * db,char * name,bool is_proc,bool no_errors)2015 bool check_routine_access(THD *thd, ulong want_access, const char *db,
2016                           char *name, bool is_proc, bool no_errors) {
2017   DBUG_TRACE;
2018   TABLE_LIST tables[1];
2019 
2020   new (&tables[0]) TABLE_LIST();
2021   tables->db = db;
2022   tables->db_length = strlen(db);
2023   tables->table_name = tables->alias = name;
2024   tables->table_name_length = strlen(tables->table_name);
2025 
2026   /*
2027     The following test is just a shortcut for check_access() (to avoid
2028     calculating db_access) under the assumption that it's common to
2029     give persons global right to execute all stored SP (but not
2030     necessary to create them).
2031     Note that this effectively bypasses the ACL_internal_schema_access checks
2032     that are implemented for the INFORMATION_SCHEMA and PERFORMANCE_SCHEMA,
2033     which are located in check_access().
2034     Since the I_S and P_S do not contain routines, this bypass is ok,
2035     as long as this code path is not abused to create routines.
2036     The assert enforce that.
2037   */
2038   DBUG_ASSERT((want_access & CREATE_PROC_ACL) == 0);
2039   if (thd->security_context()->check_access(want_access, db))
2040     tables->grant.privilege = want_access;
2041   else {
2042     DBUG_PRINT("info", ("Checking routine %s.%s for schema level access.", db,
2043                         tables->table_name));
2044     if (check_access(thd, want_access, db, &tables->grant.privilege,
2045                      &tables->grant.m_internal, false, no_errors))
2046 
2047       return true;
2048   }
2049 
2050   DBUG_PRINT("info", ("Checking routine %s.%s for routine level access.", db,
2051                       tables->table_name));
2052   return check_grant_routine(thd, want_access, tables, is_proc, no_errors);
2053 }
2054 
2055 /**
2056   Check if the given table has any of the asked privileges
2057 
2058   @param thd		 Thread handler
2059   @param want_access	 Bitmap of possible privileges to check for
2060   @param table The table for which access needs to be validated
2061   @retval
2062     0  ok
2063   @retval
2064     1  error
2065 */
2066 
check_some_access(THD * thd,ulong want_access,TABLE_LIST * table)2067 bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) {
2068   ulong access;
2069   DBUG_TRACE;
2070 
2071   /* This loop will work as long as we have less than 32 privileges */
2072   for (access = 1; access < want_access; access <<= 1) {
2073     if (access & want_access) {
2074       if (!check_access(thd, access, table->db, &table->grant.privilege,
2075                         &table->grant.m_internal, false, true) &&
2076           !check_grant(thd, access, table, false, 1, true))
2077         return false;
2078     }
2079   }
2080   DBUG_PRINT("exit", ("no matching access rights"));
2081   return true;
2082 }
2083 
2084 /**
2085   @brief Check if user has full access to view routine's properties (i.e
2086   including stored routine code). User must have GLOBAL SELECT or SHOW_ROUTINE
2087   privilege, or be the definer of this routine.
2088 
2089   @param thd                  Thread handler
2090   @param db                   Database name
2091   @param definer_user         Definer username
2092   @param definer_host         Definer host
2093 
2094   @retval false   no full access.
2095   @retval true    has full access.
2096 */
has_full_view_routine_access(THD * thd,const char * db,const char * definer_user,const char * definer_host)2097 bool has_full_view_routine_access(THD *thd, const char *db,
2098                                   const char *definer_user,
2099                                   const char *definer_host) {
2100   DBUG_TRACE;
2101   Security_context *sctx = thd->security_context();
2102 
2103   return sctx->check_access(SELECT_ACL, db) ||
2104          sctx->has_global_grant(STRING_WITH_LEN("SHOW_ROUTINE")).first ||
2105          (!strcmp(definer_user, sctx->priv_user().str) &&
2106           !my_strcasecmp(system_charset_info, definer_host,
2107                          sctx->priv_host().str));
2108 }
2109 
2110 /**
2111   @brief Check if user has partial access to view routine's properties
2112   (i.e. excluding stored routine code). User must have EXECUTE/CREATE/ALTER
2113   ROUTINE privileges.
2114 
2115   @param thd                  Thread handler
2116   @param db                   Database name
2117   @param routine_name         Routine name
2118   @param is_proc              True if this routine is a stored procedure, rather
2119   than a stored function.
2120 
2121   @retval false   no access.
2122   @retval true    has partial access.
2123 */
2124 
has_partial_view_routine_access(THD * thd,const char * db,const char * routine_name,bool is_proc)2125 bool has_partial_view_routine_access(THD *thd, const char *db,
2126                                      const char *routine_name, bool is_proc) {
2127   DBUG_TRACE;
2128 
2129   /*
2130     The following test is just a shortcut for check_access() (to avoid
2131     calculating db_access)
2132     Note that this effectively bypasses the ACL_internal_schema_access checks
2133     that are implemented for the INFORMATION_SCHEMA and PERFORMANCE_SCHEMA,
2134     which are located in check_access().
2135     Since the I_S and P_S do not contain routines, this bypass is ok,
2136     as it only opens SHOW_PROC_ACLS.
2137   */
2138   if (thd->security_context()->check_access(SHOW_PROC_ACLS, db ? db : "", true))
2139     return true;
2140 
2141   ulong save_priv;
2142   if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, nullptr, false,
2143                     true) ||
2144       (save_priv & SHOW_PROC_ACLS))
2145     return true;
2146 
2147   return !check_routine_level_acl(thd, db, routine_name, is_proc);
2148 }
2149 
2150 /**
2151   @brief Compare requested privileges with the privileges acquired from the
2152     User- and Db-tables.
2153   @param thd          Thread handler
2154   @param want_access  The requested access privileges.
2155   @param db           A pointer to the Db name.
2156   @param[out] save_priv A pointer to the granted privileges will be stored.
2157   @param grant_internal_info A pointer to the internal grant cache.
2158   @param dont_check_global_grants True if no global grants are checked.
2159   @param no_errors    True if no errors should be sent to the client.
2160 
2161   'save_priv' is used to save the User-table (global) and Db-table grants for
2162   the supplied db name. Note that we don't store db level grants if the global
2163   grants is enough to satisfy the request AND the global grants contains a
2164   SELECT grant.
2165 
2166   For internal databases (INFORMATION_SCHEMA, PERFORMANCE_SCHEMA),
2167   additional rules apply, see ACL_internal_schema_access.
2168 
2169   @see check_grant
2170 
2171   @return Status of denial of access by exclusive ACLs.
2172     @retval false Access can't exclusively be denied by Db- and User-table
2173       access unless Column- and Table-grants are checked too.
2174     @retval true Access denied. The DA is set if no_error = false!
2175 */
2176 
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)2177 bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
2178                   GRANT_INTERNAL_INFO *grant_internal_info,
2179                   bool dont_check_global_grants, bool no_errors) {
2180   Security_context *sctx = thd->security_context();
2181   ulong db_access;
2182   const std::string &db_name = db ? db : "";
2183 
2184   /*
2185     GRANT command:
2186     In case of database level grant the database name may be a pattern,
2187     in case of table|column level grant the database name can not be a pattern.
2188     We use 'dont_check_global_grants' as a flag to determine
2189     if it's database level grant command
2190     (see SQLCOM_GRANT case, mysql_execute_command() function) and
2191     set db_is_pattern according to 'dont_check_global_grants' value.
2192   */
2193   bool db_is_pattern = ((want_access & GRANT_ACL) && dont_check_global_grants);
2194   ulong dummy;
2195   DBUG_TRACE;
2196   DBUG_PRINT("enter",
2197              ("db: %s  want_access: %lu  master_access: %lu", db_name.c_str(),
2198               want_access, sctx->master_access(db_name)));
2199 
2200   if (save_priv)
2201     *save_priv = 0;
2202   else {
2203     save_priv = &dummy;
2204     dummy = 0;
2205   }
2206 
2207   THD_STAGE_INFO(thd, stage_checking_permissions);
2208   if ((!db || !db[0]) && !thd->db().str && !dont_check_global_grants) {
2209     DBUG_PRINT("error", ("No database"));
2210     if (!no_errors) my_error(ER_NO_DB_ERROR, MYF(0)); /* purecov: tested */
2211     return true;                                      /* purecov: tested */
2212   }
2213 
2214   if (db != nullptr) {
2215     if (db != any_db) {
2216       const ACL_internal_schema_access *access;
2217       access = get_cached_schema_access(grant_internal_info, db);
2218       if (access) {
2219         switch (access->check(want_access, save_priv)) {
2220           case ACL_INTERNAL_ACCESS_GRANTED:
2221             /*
2222               All the privileges requested have been granted internally.
2223               [out] *save_privileges= Internal privileges.
2224             */
2225             return false;
2226           case ACL_INTERNAL_ACCESS_DENIED:
2227             if (!no_errors) {
2228               my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user().str,
2229                        sctx->priv_host().str, db);
2230             }
2231             return true;
2232           case ACL_INTERNAL_ACCESS_CHECK_GRANT:
2233             /*
2234               Only some of the privilege requested have been granted internally,
2235               proceed with the remaining bits of the request (want_access).
2236             */
2237             want_access &= ~(*save_priv);
2238             break;
2239         }
2240       }
2241     }
2242   }
2243 
2244   if (sctx->check_access(want_access, db_name)) {
2245     /*
2246       1. If we don't have a global SELECT privilege, we have to get the
2247       database specific access rights to be able to handle queries of type
2248       UPDATE t1 SET a=1 WHERE b > 0
2249       2. Change db access if it isn't current db which is being addressed
2250     */
2251     if (!(sctx->check_access(SELECT_ACL, db_name))) {
2252       if (db &&
2253           (!thd->db().str || db_is_pattern || strcmp(db, thd->db().str))) {
2254         if (sctx->get_active_roles()->size() > 0) {
2255           bool use_patterns =
2256               ((want_access & GRANT_ACL) ? true : !dont_check_global_grants);
2257           db_access = sctx->db_acl({db, strlen(db)}, use_patterns);
2258         } else {
2259           db_access = acl_get(thd, sctx->host().str, sctx->ip().str,
2260                               sctx->priv_user().str, db, db_is_pattern);
2261         }
2262       } else {
2263         /* get access for current db */
2264         db_access = sctx->current_db_access();
2265       }
2266       /*
2267         The effective privileges are the union of the global privileges
2268         and the intersection of db- and host-privileges,
2269         plus the internal privileges.
2270       */
2271       *save_priv |= sctx->master_access(db_name) | db_access;
2272     } else
2273       *save_priv |= sctx->master_access(db_name);
2274     return false;
2275   }
2276 
2277   if (((want_access & ~sctx->master_access(db_name)) & ~DB_ACLS) ||
2278       (!db && dont_check_global_grants)) {  // We can never grant this
2279     DBUG_PRINT("error", ("No possible access"));
2280     if (!no_errors) {
2281       if (thd->password == 2)
2282         my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
2283                  sctx->priv_user().str, sctx->priv_host().str);
2284       else
2285         my_error(ER_ACCESS_DENIED_ERROR, MYF(0), sctx->priv_user().str,
2286                  sctx->priv_host().str,
2287                  (thd->password ? ER_THD(thd, ER_YES)
2288                                 : ER_THD(thd, ER_NO))); /* purecov: tested */
2289     }
2290     return true; /* purecov: tested */
2291   }
2292 
2293   if (db == any_db) {
2294     /*
2295       Access granted; Allow select on *any* db.
2296       [out] *save_privileges= 0
2297     */
2298     return false;
2299   }
2300 
2301   if (db && (!thd->db().str || db_is_pattern || strcmp(db, thd->db().str))) {
2302     if (sctx->get_active_roles()->size() > 0) {
2303       bool flags =
2304           ((want_access & GRANT_ACL) ? true : !dont_check_global_grants);
2305       db_access = sctx->db_acl({db, strlen(db)}, flags);
2306       DBUG_PRINT("info", ("check_access using db-level privilege for %s. "
2307                           "ACL: %lu",
2308                           db, db_access));
2309     } else {
2310       db_access = acl_get(thd, sctx->host().str, sctx->ip().str,
2311                           sctx->priv_user().str, db, db_is_pattern);
2312     }
2313   } else
2314     db_access = sctx->current_db_access();
2315   DBUG_PRINT("info",
2316              ("db_access: %lu  want_access: %lu", db_access, want_access));
2317 
2318   /*
2319     Save the union of User-table and the intersection between Db-table and
2320     Host-table privileges, with the already saved internal privileges.
2321   */
2322   db_access = (db_access | sctx->master_access(db_name));
2323   *save_priv |= db_access;
2324 
2325   /*
2326     We need to investigate column- and table access if all requested privileges
2327     belongs to the bit set of .
2328   */
2329   bool need_table_or_column_check =
2330       (want_access & (TABLE_ACLS | PROC_ACLS | db_access)) == want_access;
2331   /*
2332     Grant access if the requested access is in the intersection of
2333     host- and db-privileges (as retrieved from the acl cache),
2334     also grant access if all the requested privileges are in the union of
2335     TABLES_ACLS and PROC_ACLS; see check_grant.
2336   */
2337   if (((db_access & want_access) == want_access) ||
2338       (!dont_check_global_grants && need_table_or_column_check)) {
2339     /*
2340        Ok; but need to check table- and column privileges.
2341        [out] *save_privileges is (User-priv | (Db-priv & Host-priv) |
2342        Internal-priv)
2343     */
2344     return false;
2345   }
2346 
2347   /*
2348     Access is denied;
2349     [out] *save_privileges is (User-priv | (Db-priv & Host-priv) |
2350     Internal-priv)
2351   */
2352   DBUG_PRINT("error", ("Access denied"));
2353   if (!no_errors)
2354     my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user().str,
2355              sctx->priv_host().str,
2356              (db ? db : (thd->db().str ? thd->db().str : "unknown")));
2357   return true;
2358 }
2359 
2360 /**
2361   @brief Check if the requested privileges exists in either User-, DB- or,
2362     tables- tables.
2363   @param thd          Thread context
2364   @param requirements Privileges requested
2365   @param tables       List of tables to be compared against
2366   @param no_errors    Don't report error to the client (using my_error() call).
2367   @param any_combination_of_privileges_will_do true if any privileges on any
2368     column combination is enough.
2369   @param number       Only the first 'number' tables in the linked list are
2370                       relevant.
2371 
2372   The supplied table list contains cached privileges. This functions calls the
2373   help functions check_access and check_grant to verify the first three steps
2374   in the privileges check queue:
2375   1. Global privileges
2376   2. OR (db privileges AND host privileges)
2377   3. OR table privileges
2378   4. OR column privileges (not checked by this function!)
2379   5. OR routine privileges (not checked by this function!)
2380 
2381   @see check_access
2382   @see check_grant
2383 
2384   @note This functions assumes that table list used and
2385   thd->lex->query_tables_own_last value correspond to each other
2386   (the latter should be either 0 or point to next_global member
2387   of one of elements of this table list).
2388 
2389   @retval false OK
2390   @retval true  Access denied; But column or routine privileges might need to
2391       be checked also.
2392 */
2393 
check_table_access(THD * thd,ulong requirements,TABLE_LIST * tables,bool any_combination_of_privileges_will_do,uint number,bool no_errors)2394 bool check_table_access(THD *thd, ulong requirements, TABLE_LIST *tables,
2395                         bool any_combination_of_privileges_will_do, uint number,
2396                         bool no_errors) {
2397   DBUG_TRACE;
2398   TABLE_LIST *org_tables = tables;
2399   TABLE_LIST *first_not_own_table = thd->lex->first_not_own_table();
2400   uint i = 0;
2401   Security_context *sctx = thd->security_context();
2402   Security_context *backup_ctx = thd->security_context();
2403 
2404   /*
2405     The check that first_not_own_table is not reached is for the case when
2406     the given table list refers to the list for prelocking (contains tables
2407     of other queries). For simple queries first_not_own_table is 0.
2408   */
2409   for (; i < number && tables != first_not_own_table && tables;
2410        tables = tables->next_global, i++) {
2411     TABLE_LIST *const table_ref =
2412         tables->correspondent_table ? tables->correspondent_table : tables;
2413     ulong want_access = requirements;
2414     if (table_ref->security_ctx)
2415       sctx = table_ref->security_ctx;
2416     else
2417       sctx = backup_ctx;
2418 
2419     /*
2420       We should not encounter table list elements for reformed SHOW
2421       statements unless this is first table list element in the main
2422       select.
2423       Such table list elements require additional privilege check
2424       (see check_show_access()). This check is carried out by caller,
2425       but only for the first table list element from the main select.
2426     */
2427     DBUG_ASSERT(!table_ref->schema_table_reformed ||
2428                 table_ref == thd->lex->select_lex->table_list.first);
2429 
2430     DBUG_PRINT("info",
2431                ("table: %s derived: %d  view: %d", table_ref->table_name,
2432                 table_ref->is_derived(), table_ref->is_view()));
2433 
2434     if (table_ref->is_internal()) continue;
2435 
2436     thd->set_security_context(sctx);
2437 
2438     if (check_access(thd, want_access, table_ref->get_db_name(),
2439                      &table_ref->grant.privilege, &table_ref->grant.m_internal,
2440                      false, no_errors))
2441       goto deny;
2442   }
2443   thd->set_security_context(backup_ctx);
2444 
2445   DBUG_EXECUTE_IF("force_check_table_access_return_ok", return false;);
2446   return check_grant(thd, requirements, org_tables,
2447                      any_combination_of_privileges_will_do, number, no_errors);
2448 deny:
2449   thd->set_security_context(backup_ctx);
2450   return true;
2451 }
2452 
2453 /**
2454   Check if a current user has the privilege TABLE_ENCRYPTION_ADMIN required
2455   to create encrypted table. We skip the same for slave threads.
2456 
2457   @param thd    Current thread
2458 
2459   @retval false  A user has the privilege TABLE_ENCRYPTION_ADMIN
2460   @retval true   A user doesn't have the privilege TABLE_ENCRYPTION_ADMIN
2461 */
2462 
check_table_encryption_admin_access(THD * thd)2463 bool check_table_encryption_admin_access(THD *thd) {
2464   Security_context *sctx = thd->security_context();
2465 
2466   /* replication slave thread can do anything */
2467   if (thd->slave_thread) {
2468     return false;
2469   }
2470 
2471   if (!sctx->has_global_grant(STRING_WITH_LEN("TABLE_ENCRYPTION_ADMIN"))
2472            .first) {
2473     return true;
2474   }
2475 
2476   return false;
2477 }
2478 
2479 /**
2480   Given a TABLE_LIST object this function checks against
2481    1. global privileges
2482    2. db privileges
2483    3. table level privileges
2484 
2485   This function only checks the existence of required ACL on a single table
2486   object. No special consideration is made for the table type (derived, view,
2487   temporary etc).
2488 
2489   @param thd Thread handle
2490   @param required_acl The privileges which are required to continue
2491   @param table An initialized, single TABLE_LIST object
2492 
2493   @retval true Access is granted
2494   @retval false Access denied
2495 */
2496 
is_granted_table_access(THD * thd,ulong required_acl,TABLE_LIST * table)2497 bool is_granted_table_access(THD *thd, ulong required_acl, TABLE_LIST *table) {
2498   DBUG_TRACE;
2499   const char *table_name = table->get_table_name();
2500   const char *db_name = table->get_db_name();
2501   if (thd->security_context()->get_active_roles()->size() != 0) {
2502     /* Check privilege against the role privilege cache */
2503     ulong global_acl = thd->security_context()->master_access(db_name);
2504     if ((global_acl & required_acl) == required_acl) {
2505       DBUG_PRINT("info", ("Access granted for %s.%s by global privileges",
2506                           db_name, table_name));
2507       return true;
2508     }
2509     ulong db_acl =
2510         thd->security_context()->db_acl({db_name, strlen(db_name)}, true) |
2511         global_acl;
2512     if ((db_acl & required_acl) == required_acl) {
2513       DBUG_PRINT("info", ("Access granted for %s.%s by schema privileges",
2514                           db_name, table_name));
2515       return true;
2516     }
2517 
2518     Grant_table_aggregate aggr = thd->security_context()->table_and_column_acls(
2519         {table_name, strlen(table_name)}, {db_name, strlen(db_name)});
2520     if (((aggr.table_access | db_acl) & required_acl) == required_acl) {
2521       DBUG_PRINT("info", ("Access granted for %s.%s by table privileges",
2522                           db_name, table_name));
2523       return true;
2524     }
2525   } else {
2526     /* No active roles */
2527     Security_context *sctx = thd->security_context();
2528     if ((sctx->master_access(db_name) & required_acl) == required_acl) {
2529       DBUG_PRINT("info",
2530                  ("(no role) Access granted for %s.%s by global privileges",
2531                   db_name, table_name));
2532       return true;
2533     }
2534     ulong db_access;
2535     if ((!thd->db().str || strcmp(db_name, thd->db().str)))
2536       db_access = acl_get(thd, sctx->host().str, sctx->ip().str,
2537                           sctx->priv_user().str, db_name, false);
2538     else
2539       db_access = sctx->current_db_access();
2540     db_access = (db_access | sctx->master_access(db_name));
2541     if ((db_access & required_acl) == required_acl) {
2542       DBUG_PRINT("info",
2543                  ("(no role)  Access granted for %s.%s by schema privileges",
2544                   db_name, table_name));
2545       return true;
2546     }
2547     GRANT_TABLE *grant_table =
2548         table_hash_search(sctx->host().str, sctx->ip().str, db_name,
2549                           sctx->priv_user().str, table_name, false);
2550     if (!grant_table) return false;
2551 
2552     if (((grant_table->privs | db_access) & required_acl) == required_acl) {
2553       DBUG_PRINT("info",
2554                  ("(no role) Access granted for %s.%s by table privileges",
2555                   db_name, table_name));
2556       return true;
2557     }
2558   }
2559   // permission denied
2560   return false;
2561 }
2562 
2563 /****************************************************************************
2564   Handle GRANT commands
2565 ****************************************************************************/
2566 
has_grant_role_privilege(THD * thd,const List<LEX_USER> * roles)2567 bool has_grant_role_privilege(THD *thd, const List<LEX_USER> *roles) {
2568   DBUG_TRACE;
2569   Security_context *sctx = thd->security_context();
2570 
2571   /* 1. user has global ROLE_ADMIN or SUPER_ACL privileges */
2572   if (sctx->check_access(SUPER_ACL) ||
2573       sctx->has_global_grant(STRING_WITH_LEN("ROLE_ADMIN")).first) {
2574     return true;
2575   }
2576 
2577   LEX_USER *role, *tmp_role_name;
2578   List_iterator<LEX_USER> role_list(const_cast<List<LEX_USER> &>(*roles));
2579   std::vector<LEX_USER *> remaining_roles;
2580 
2581   while ((tmp_role_name = role_list++)) {
2582     if (!(role = get_current_user(thd, tmp_role_name))) {
2583       remaining_roles.push_back(tmp_role_name);
2584       continue;
2585     }
2586     /*
2587       2. user has inherited the GRANT r TO CURRENT_USER WITH ADMIN OPTION
2588          privileges, where r is a node in some active role graph R granted
2589          to CURRENT_USER.
2590     */
2591 
2592     if (!sctx->has_with_admin_acl(role->user, role->host)) {
2593       remaining_roles.push_back(tmp_role_name);
2594     }
2595   }
2596 
2597   if (remaining_roles.size() == 0) return true;
2598 
2599   /* Check if role was granted with WITH ADMIN option */
2600   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
2601   if (!acl_cache_lock.lock(false)) return false;
2602   List_of_granted_roles granted_roles;
2603   LEX_USER user;
2604   user.user = sctx->priv_user();
2605   user.host = sctx->priv_host();
2606   get_granted_roles(&user, &granted_roles);
2607 
2608   for (auto tmp_role : remaining_roles) {
2609     if (!(role = get_current_user(thd, tmp_role))) return false;
2610     std::string role_id(create_authid_str_from(role));
2611     auto it = find(granted_roles.begin(), granted_roles.end(), role_id);
2612     if (it == granted_roles.end()) return false;
2613     if (it->second != true) return false;
2614   }
2615 
2616   return true;
2617 }
2618 
2619 /*
2620   Store table level and column level grants in the privilege tables
2621 
2622   SYNOPSIS
2623     mysql_table_grant()
2624     thd                 Thread handle
2625     table_list          List of tables to give grant
2626     user_list           List of users to give grant
2627     columns             List of columns to give grant
2628     rights              Table level grant
2629     revoke_grant        Set to true if this is a REVOKE command
2630 
2631   RETURN
2632     false ok
2633     true  error
2634 */
2635 
mysql_table_grant(THD * thd,TABLE_LIST * table_list,List<LEX_USER> & user_list,List<LEX_COLUMN> & columns,ulong rights,bool revoke_grant)2636 int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
2637                       List<LEX_USER> &user_list, List<LEX_COLUMN> &columns,
2638                       ulong rights, bool revoke_grant) {
2639   ulong column_priv = 0;
2640   List_iterator<LEX_USER> str_list(user_list);
2641   LEX_USER *Str, *tmp_Str;
2642   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
2643   const char *db_name, *table_name;
2644   bool transactional_tables;
2645   acl_table::Pod_user_what_to_update what_to_set;
2646   bool result = false;
2647   int ret = 0;
2648   std::set<LEX_USER *> existing_users;
2649 
2650   DBUG_TRACE;
2651 
2652   DBUG_ASSERT(initialized);
2653 
2654   if (rights & ~TABLE_ACLS) {
2655     my_error(ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0));
2656     return true;
2657   }
2658 
2659   if (!revoke_grant) {
2660     if (columns.elements) {
2661       class LEX_COLUMN *column;
2662       List_iterator<LEX_COLUMN> column_iter(columns);
2663 
2664       if (open_tables_for_query(thd, table_list, 0)) return true;
2665 
2666       if (table_list->is_view()) {
2667         if (table_list->resolve_derived(thd, false))
2668           return true; /* purecov: inspected */
2669 
2670         // Prepare a readonly (materialized) view for access to columns
2671         if (table_list->setup_materialized_derived(thd))
2672           return true; /* purecov: inspected */
2673       }
2674       while ((column = column_iter++)) {
2675         uint unused_field_idx = NO_CACHED_FIELD_INDEX;
2676         TABLE_LIST *dummy;
2677         Field *f = find_field_in_table_ref(
2678             thd, table_list, column->column.ptr(), column->column.length(),
2679             column->column.ptr(), nullptr, nullptr, nullptr,
2680             // check that we have the
2681             // to-be-granted privilege:
2682             column->rights, false, &unused_field_idx, false, &dummy);
2683         if (f == (Field *)nullptr) {
2684           my_error(ER_BAD_FIELD_ERROR, MYF(0), column->column.c_ptr(),
2685                    table_list->alias);
2686           return true;
2687         }
2688         if (f == (Field *)-1) return true;
2689         column_priv |= column->rights;
2690       }
2691       close_mysql_tables(thd);
2692     } else {
2693       if (!(rights & CREATE_ACL)) {
2694         // We need at least a shared MDL lock on the table to be allowed
2695         // to safely check its existence.
2696         MDL_request mdl_request;
2697         MDL_REQUEST_INIT(&mdl_request, MDL_key::TABLE, table_list->db,
2698                          table_list->table_name, MDL_SHARED, MDL_TRANSACTION);
2699         if (thd->mdl_context.acquire_lock(&mdl_request,
2700                                           thd->variables.lock_wait_timeout))
2701           return true;
2702 
2703         bool exists;
2704         if (dd::table_exists(thd->dd_client(), table_list->db,
2705                              table_list->table_name, &exists))
2706           return true;
2707 
2708         if (!exists) {
2709           my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
2710           return true;
2711         }
2712       }
2713       ulong missing_privilege = rights & ~table_list->grant.privilege;
2714       if (missing_privilege) {
2715         char command[128];
2716         get_privilege_desc(command, sizeof(command), missing_privilege);
2717         my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), command,
2718                  thd->security_context()->priv_user().str,
2719                  thd->security_context()->host_or_ip().str, table_list->alias);
2720         return true;
2721       }
2722     }
2723   }
2724 
2725   /*
2726     This statement will be replicated as a statement, even when using
2727     row-based replication.  The binlog state will be cleared here to
2728     statement based replication and will be reset to the originals
2729     values when we are out of this function scope
2730   */
2731   Save_and_Restore_binlog_format_state binlog_format_state(thd);
2732 
2733   /*
2734     The lock api is depending on the thd->lex variable which needs to be
2735     re-initialized.
2736   */
2737   Query_tables_list backup;
2738   thd->lex->reset_n_backup_query_tables_list(&backup);
2739   /*
2740     Restore Query_tables_list::sql_command value, which was reset
2741     above, as the code writing query to the binary log assumes that
2742     this value corresponds to the statement being executed.
2743   */
2744   thd->lex->sql_command = backup.sql_command;
2745 
2746   { /* Critical Section */
2747     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
2748 
2749     if ((ret = open_grant_tables(thd, tables, &transactional_tables))) {
2750       thd->lex->restore_backup_query_tables_list(&backup);
2751       return ret != 1; /* purecov: deadcode */
2752     }
2753 
2754     if (!acl_cache_lock.lock()) {
2755       commit_and_close_mysql_tables(thd);
2756       return true;
2757     }
2758 
2759     if (check_system_user_privilege(thd, user_list)) {
2760       commit_and_close_mysql_tables(thd);
2761       return true;
2762     }
2763 
2764     MEM_ROOT *old_root = thd->mem_root;
2765     thd->mem_root = &memex;
2766     grant_version++;
2767 
2768     while ((tmp_Str = str_list++)) {
2769       int error;
2770       GRANT_TABLE *grant_table;
2771 
2772       if (!(Str = get_current_user(thd, tmp_Str))) {
2773         result = true;
2774         continue;
2775       }
2776 
2777       Userhostpassword_list password_list;
2778       if (set_and_validate_user_attributes(
2779               thd, Str, what_to_set, false, false,
2780               &tables[ACL_TABLES::TABLE_PASSWORD_HISTORY], nullptr,
2781               revoke_grant ? "REVOKE" : "GRANT", password_list)) {
2782         result = true;
2783         continue;
2784       }
2785 
2786       ACL_USER *this_user = find_acl_user(Str->host.str, Str->user.str, true);
2787       if (this_user && (what_to_set.m_what & PLUGIN_ATTR))
2788         existing_users.insert(tmp_Str);
2789 
2790       db_name = table_list->get_db_name();
2791       thd->add_to_binlog_accessed_dbs(db_name);  // collecting db:s for MTS
2792       table_name = table_list->get_table_name();
2793 
2794       /* Find/create cached table grant */
2795       grant_table = table_hash_search(Str->host.str, NullS, db_name,
2796                                       Str->user.str, table_name, true);
2797       if (!grant_table) {
2798         if (revoke_grant) {
2799           my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0), Str->user.str,
2800                    Str->host.str, table_list->table_name);
2801           result = true;
2802           continue;
2803         }
2804 
2805         DBUG_EXECUTE_IF("mysql_table_grant_out_of_memory",
2806                         DBUG_SET("+d,simulate_out_of_memory"););
2807         grant_table = new (thd->mem_root)
2808             GRANT_TABLE(Str->host.str, db_name, Str->user.str, table_name,
2809                         rights, column_priv);
2810         DBUG_EXECUTE_IF("mysql_table_grant_out_of_memory",
2811                         DBUG_SET("-d,simulate_out_of_memory"););
2812 
2813         if (!grant_table) {
2814           result = true; /* purecov: deadcode */
2815           break;         /* purecov: deadcode */
2816         }
2817         column_priv_hash->emplace(
2818             grant_table->hash_key,
2819             unique_ptr_destroy_only<GRANT_TABLE>(grant_table));
2820       }
2821 
2822       /* If revoke_grant, calculate the new column privilege for tables_priv */
2823       if (revoke_grant) {
2824         class LEX_COLUMN *column;
2825         List_iterator<LEX_COLUMN> column_iter(columns);
2826         GRANT_COLUMN *grant_column;
2827 
2828         /* Fix old grants */
2829         while ((column = column_iter++)) {
2830           grant_column = column_hash_search(grant_table, column->column.ptr(),
2831                                             column->column.length());
2832           if (grant_column) grant_column->rights &= ~(column->rights | rights);
2833         }
2834         /* scan trough all columns to get new column grant */
2835         column_priv = 0;
2836         for (const auto &key_and_value : grant_table->hash_columns) {
2837           grant_column = key_and_value.second.get();
2838           grant_column->rights &= ~rights;  // Fix other columns
2839           column_priv |= grant_column->rights;
2840         }
2841       } else {
2842         column_priv |= grant_table->cols;
2843       }
2844 
2845       /* update table and columns */
2846 
2847       // Hold on to grant_table if it gets deleted, since we use it below.
2848       std::unique_ptr<GRANT_TABLE, Destroy_only<GRANT_TABLE>>
2849           deleted_grant_table;
2850 
2851       if ((error = replace_table_table(
2852                thd, grant_table, &deleted_grant_table,
2853                tables[ACL_TABLES::TABLE_TABLES_PRIV].table, *Str, db_name,
2854                table_name, rights, column_priv, revoke_grant))) {
2855         result = true;
2856         if (error < 0) break;
2857 
2858         continue;
2859       }
2860 
2861       if (tables[3].table) {
2862         if ((error = replace_column_table(
2863                  thd, grant_table, tables[ACL_TABLES::TABLE_COLUMNS_PRIV].table,
2864                  *Str, columns, db_name, table_name, rights, revoke_grant))) {
2865           result = true;
2866           if (error < 0) break;
2867 
2868           continue;
2869         }
2870       }
2871     }
2872     thd->mem_root = old_root;
2873 
2874     DBUG_ASSERT(!result || thd->is_error());
2875 
2876     result = log_and_commit_acl_ddl(thd, transactional_tables);
2877 
2878     {
2879       /* Notify audit plugin. We will ignore the return value. */
2880       LEX_USER *existing_user;
2881       for (LEX_USER *one_user : existing_users) {
2882         if ((existing_user = get_current_user(thd, one_user)))
2883           mysql_audit_notify(
2884               thd, AUDIT_EVENT(MYSQL_AUDIT_AUTHENTICATION_CREDENTIAL_CHANGE),
2885               thd->is_error(), existing_user->user.str, existing_user->host.str,
2886               existing_user->plugin.str, is_role_id(existing_user), nullptr,
2887               nullptr);
2888       }
2889     }
2890     get_global_acl_cache()->increase_version();
2891   } /* Critical section */
2892 
2893   if (!result) {
2894     my_ok(thd);
2895     /* Notify storage engines */
2896     acl_notify_htons(thd, revoke_grant ? SQLCOM_REVOKE : SQLCOM_GRANT,
2897                      &user_list);
2898   }
2899 
2900   thd->lex->restore_backup_query_tables_list(&backup);
2901   DEBUG_SYNC(thd, "after_table_grant_revoke");
2902   return result;
2903 }
2904 
2905 /**
2906   Store routine level grants in the privilege tables
2907 
2908   @param thd Thread handle
2909   @param table_list List of routines to give grant
2910   @param is_proc Is this a list of procedures?
2911   @param user_list List of users to give grant
2912   @param rights Table level grant
2913   @param revoke_grant Is this is a REVOKE command?
2914   @param write_to_binlog True if this statement should be written to binlog
2915 
2916   @retval false Success.
2917   @retval true An error occurred.
2918 */
2919 
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)2920 bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
2921                          List<LEX_USER> &user_list, ulong rights,
2922                          bool revoke_grant, bool write_to_binlog) {
2923   List_iterator<LEX_USER> str_list(user_list);
2924   LEX_USER *Str, *tmp_Str;
2925   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
2926   const char *db_name, *table_name;
2927   bool transactional_tables;
2928   acl_table::Pod_user_what_to_update what_to_set;
2929   bool result = false;
2930   int ret;
2931   std::set<LEX_USER *> existing_users;
2932 
2933   DBUG_TRACE;
2934 
2935   DBUG_ASSERT(initialized);
2936 
2937   if (rights & ~PROC_ACLS) {
2938     my_error(ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0));
2939     return true;
2940   }
2941 
2942   if (!revoke_grant) {
2943     if (sp_exist_routines(thd, table_list, is_proc)) return true;
2944   }
2945 
2946   /*
2947     This statement will be replicated as a statement, even when using
2948     row-based replication.  The binlog state will be cleared here to
2949     statement based replication and will be reset to the originals
2950     values when we are out of this function scope
2951   */
2952   Save_and_Restore_binlog_format_state binlog_format_state(thd);
2953   if ((ret = open_grant_tables(thd, tables, &transactional_tables)))
2954     return ret != 1;
2955 
2956   { /* Critical section */
2957     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
2958 
2959     if (!acl_cache_lock.lock()) {
2960       commit_and_close_mysql_tables(thd);
2961       return true;
2962     }
2963 
2964     if (check_system_user_privilege(thd, user_list)) {
2965       commit_and_close_mysql_tables(thd);
2966       return true;
2967     }
2968 
2969     MEM_ROOT *old_root = thd->mem_root;
2970     thd->mem_root = &memex;
2971 
2972     DBUG_PRINT("info", ("now time to iterate and add users"));
2973 
2974     while ((tmp_Str = str_list++)) {
2975       int error;
2976       GRANT_NAME *grant_name;
2977 
2978       if (!(Str = get_current_user(thd, tmp_Str))) {
2979         result = true;
2980         continue;
2981       }
2982 
2983       Userhostpassword_list password_list;
2984       if (set_and_validate_user_attributes(
2985               thd, Str, what_to_set, false, false,
2986               &tables[ACL_TABLES::TABLE_PASSWORD_HISTORY], nullptr,
2987               revoke_grant ? "REVOKE" : "GRANT", password_list)) {
2988         result = true;
2989         continue;
2990       }
2991 
2992       ACL_USER *this_user = find_acl_user(Str->host.str, Str->user.str, true);
2993       if (this_user && (what_to_set.m_what & PLUGIN_ATTR))
2994         existing_users.insert(tmp_Str);
2995 
2996       db_name = table_list->db;
2997       if (write_to_binlog) thd->add_to_binlog_accessed_dbs(db_name);
2998       table_name = table_list->table_name;
2999       grant_name =
3000           routine_hash_search(Str->host.str, NullS, db_name, Str->user.str,
3001                               table_name, is_proc, true);
3002       if (!grant_name) {
3003         if (revoke_grant) {
3004           my_error(ER_NONEXISTING_PROC_GRANT, MYF(0), Str->user.str,
3005                    Str->host.str, table_name);
3006           result = true;
3007           continue;
3008         }
3009         grant_name = new (thd->mem_root) GRANT_NAME(
3010             Str->host.str, db_name, Str->user.str, table_name, rights, true);
3011         if (!grant_name) {
3012           result = true;
3013           break;
3014         }
3015         if (is_proc)
3016           proc_priv_hash->emplace(
3017               grant_name->hash_key,
3018               unique_ptr_destroy_only<GRANT_NAME>(grant_name));
3019         else
3020           func_priv_hash->emplace(
3021               grant_name->hash_key,
3022               unique_ptr_destroy_only<GRANT_NAME>(grant_name));
3023       }
3024 
3025       if ((error = replace_routine_table(thd, grant_name, tables[4].table, *Str,
3026                                          db_name, table_name, is_proc, rights,
3027                                          revoke_grant))) {
3028         result = true;  // Remember error
3029         if (error < 0) break;
3030 
3031         continue;
3032       }
3033     }
3034     thd->mem_root = old_root;
3035 
3036     /*
3037       mysql_routine_grant can be called in following scenarios:
3038       1. As a part of GRANT statement
3039       2. As a part of CREATE PROCEDURE/ROUTINE statement
3040 
3041       In case of 2, even if we fail to grant permission on
3042       newly created routine, it is not a critical error and
3043       is suppressed by caller. Instead, a warning is thrown
3044       to user.
3045 
3046       So, if we are here and result is set to true, either of the following must
3047       be true:
3048       1. An error is set in THD
3049       2. Current statement is SQLCOM_CREATE_PROCEDURE or
3050          SQLCOM_CREATE_SPFUNCTION
3051 
3052       So assert for the same.
3053     */
3054     DBUG_ASSERT(!result || thd->is_error() ||
3055                 thd->lex->sql_command == SQLCOM_CREATE_PROCEDURE ||
3056                 thd->lex->sql_command == SQLCOM_CREATE_SPFUNCTION);
3057 
3058     result = log_and_commit_acl_ddl(thd, transactional_tables, nullptr, nullptr,
3059                                     result, write_to_binlog);
3060 
3061     {
3062       /* Notify audit plugin. We will ignore the return value. */
3063       for (LEX_USER *one_user : existing_users) {
3064         LEX_USER *existing_user;
3065         if ((existing_user = get_current_user(thd, one_user)))
3066           mysql_audit_notify(
3067               thd, AUDIT_EVENT(MYSQL_AUDIT_AUTHENTICATION_CREDENTIAL_CHANGE),
3068               thd->is_error(), existing_user->user.str, existing_user->host.str,
3069               existing_user->plugin.str, is_role_id(existing_user), nullptr,
3070               nullptr);
3071       }
3072     }
3073     get_global_acl_cache()->increase_version();
3074   } /* Critical section */
3075 
3076   /* Notify storage engines */
3077   if (write_to_binlog && !result) {
3078     acl_notify_htons(thd, revoke_grant ? SQLCOM_REVOKE : SQLCOM_GRANT,
3079                      &user_list);
3080   }
3081 
3082   return result;
3083 }
3084 
mysql_revoke_role(THD * thd,const List<LEX_USER> * users,const List<LEX_USER> * roles)3085 bool mysql_revoke_role(THD *thd, const List<LEX_USER> *users,
3086                        const List<LEX_USER> *roles) {
3087   DBUG_TRACE;
3088 
3089   /*
3090     This statement will be replicated as a statement, even when using
3091     row-based replication.  The binlog state will be cleared here to
3092     statement based replication and will be reset to the originals
3093     values when we are out of this function scope
3094   */
3095   Save_and_Restore_binlog_format_state binlog_format_state(thd);
3096   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
3097   List_iterator<LEX_USER> users_it(const_cast<List<LEX_USER> &>(*users));
3098   List_iterator<LEX_USER> roles_it(const_cast<List<LEX_USER> &>(*roles));
3099   bool errors = false;
3100   LEX_USER *lex_user;
3101   TABLE *table = nullptr;
3102   int ret;
3103   bool transactional_tables;
3104   if ((ret = open_grant_tables(thd, tables, &transactional_tables)))
3105     return ret != 1; /* purecov: deadcode */
3106 
3107   { /* Critical section */
3108     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
3109 
3110     if (!acl_cache_lock.lock()) {
3111       commit_and_close_mysql_tables(thd);
3112       return true;
3113     }
3114 
3115     if (check_system_user_privilege(thd, *users)) {
3116       commit_and_close_mysql_tables(thd);
3117       return true;
3118     }
3119 
3120     table = tables[ACL_TABLES::TABLE_ROLE_EDGES].table;
3121 
3122     std::vector<Role_id> mandatory_roles;
3123     get_mandatory_roles(&mandatory_roles);
3124     while (LEX_USER *mand_role = roles_it++) {
3125       if (std::find_if(mandatory_roles.begin(), mandatory_roles.end(),
3126                        [&](const Role_id &id) -> bool {
3127                          Role_id id2(mand_role->user, mand_role->host);
3128                          return id == id2;
3129                        }) != mandatory_roles.end()) {
3130         Role_id authid(mand_role->user, mand_role->host);
3131         std::string out;
3132         authid.auth_str(&out);
3133         my_error(ER_MANDATORY_ROLE, MYF(0), out.c_str());
3134         return true;
3135       }
3136     }
3137     while ((lex_user = users_it++) && !errors) {
3138       roles_it.rewind();
3139       if (lex_user->user.str == nullptr) {
3140         // HACK: We're using CURRENT_USER()
3141         lex_user = get_current_user(thd, lex_user);
3142         DBUG_PRINT("note", ("current user= %s@%s", lex_user->user.str,
3143                             lex_user->host.str));
3144       }
3145 
3146       ACL_USER *acl_user;
3147       if ((acl_user = find_acl_user(lex_user->host.str, lex_user->user.str,
3148                                     true)) == nullptr) {
3149         my_error(ER_UNKNOWN_AUTHID, MYF(0), lex_user->user.str,
3150                  lex_user->host.str);
3151         errors = true;
3152         break;
3153       }
3154       LEX_USER *role;
3155       while ((role = roles_it++) && !errors) {
3156         ACL_USER *acl_role;
3157         if ((acl_role = find_acl_user(role->host.str, role->user.str, true)) ==
3158             nullptr) {
3159           my_error(ER_UNKNOWN_AUTHID, MYF(0), role->user.str, role->host.str);
3160           errors = true;
3161           break;
3162         } else {
3163           DBUG_PRINT("info", ("User %s@%s will drop parent %s@%s",
3164                               acl_user->user, acl_user->host.get_host(),
3165                               role->user.str, role->host.str));
3166           Security_context *sctx = thd->security_context();
3167           if (sctx->can_operate_with({role}, consts::system_user)) {
3168             errors = true;
3169             break;
3170           }
3171 
3172           Auth_id_ref from_user = create_authid_from(role);
3173           Auth_id_ref to_user = create_authid_from(acl_user);
3174           errors = modify_role_edges_in_table(thd, table, from_user, to_user,
3175                                               false, true);
3176           if (errors) {
3177             my_error(ER_FAILED_REVOKE_ROLE, MYF(0), role->user.str,
3178                      role->host.str);
3179             break;
3180           }
3181           revoke_role(thd, acl_role, acl_user);
3182           drop_default_role_policy(
3183               thd, tables[ACL_TABLES::TABLE_DEFAULT_ROLES].table,
3184               create_authid_from(acl_role), create_authid_from(acl_user));
3185         }
3186       }
3187     }
3188     DBUG_ASSERT(!errors || thd->is_error());
3189 
3190     errors = log_and_commit_acl_ddl(thd, transactional_tables);
3191 
3192     get_global_acl_cache()->increase_version();
3193   } /* Critical section */
3194 
3195   if (!errors) {
3196     my_ok(thd);
3197     /* Notify storage engines */
3198     acl_notify_htons(thd, SQLCOM_REVOKE, users);
3199   }
3200 
3201   return false;
3202 }
3203 
has_dynamic_privilege_grant_option(Security_context * sctx,std::string priv)3204 bool has_dynamic_privilege_grant_option(Security_context *sctx,
3205                                         std::string priv) {
3206   return sctx->has_global_grant(priv.c_str(), priv.length()).second;
3207 }
3208 
3209 /**
3210   Grants a list of roles to a list of users. Changes are persistent and written
3211   in the mysql.roles_edges table.
3212 
3213   @param thd Thread handler
3214   @param users A list of authorization IDs
3215   @param roles A list of authorization IDs
3216   @param with_admin_opt True if the granted users should be able to pass on
3217     the roles to other authorization IDs
3218 
3219   @return Success state
3220     @retval true An error occurred and the DA is set.
3221     @retval false The operation was successful and DA is set.
3222 */
3223 
mysql_grant_role(THD * thd,const List<LEX_USER> * users,const List<LEX_USER> * roles,bool with_admin_opt)3224 bool mysql_grant_role(THD *thd, const List<LEX_USER> *users,
3225                       const List<LEX_USER> *roles, bool with_admin_opt) {
3226   DBUG_TRACE;
3227   /*
3228     This statement will be replicated as a statement, even when using
3229     row-based replication.  The binlog state will be cleared here to
3230     statement based replication and will be reset to the originals
3231     values when we are out of this function scope
3232   */
3233   Save_and_Restore_binlog_format_state binlog_format_state(thd);
3234   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
3235   List_iterator<LEX_USER> users_it(const_cast<List<LEX_USER> &>(*users));
3236   bool errors = false;
3237   LEX_USER *lex_user;
3238   TABLE *table = nullptr;
3239   int ret;
3240   bool transactional_tables;
3241 
3242   if ((ret = open_grant_tables(thd, tables, &transactional_tables)))
3243     return ret != 1; /* purecov: deadcode */
3244 
3245   { /* Critical section */
3246     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
3247 
3248     if (!acl_cache_lock.lock()) {
3249       commit_and_close_mysql_tables(thd);
3250       return true;
3251     }
3252 
3253     if (check_system_user_privilege(thd, *users)) {
3254       commit_and_close_mysql_tables(thd);
3255       return true;
3256     }
3257 
3258     table = tables[6].table;
3259 
3260     while ((lex_user = users_it++) && !errors) {
3261       List_iterator<LEX_USER> roles_it(const_cast<List<LEX_USER> &>(*roles));
3262       LEX_USER *role;
3263       if (lex_user->user.str == nullptr) {
3264         // HACK: We're using CURRENT_USER()
3265         lex_user = get_current_user(thd, lex_user);
3266         DBUG_PRINT("note", ("current user= %s@%s", lex_user->user.str,
3267                             lex_user->host.str));
3268       } else if (lex_user->user.length == 0 || *(lex_user->user.str) == '\0') {
3269         /* Granting roles to an anonymous user isn't allowed */
3270         my_error(ER_CANNOT_GRANT_ROLES_TO_ANONYMOUS_USER, MYF(0));
3271         return true;
3272       }
3273 
3274       ACL_USER *acl_user;
3275       if ((acl_user = find_acl_user(lex_user->host.str, lex_user->user.str,
3276                                     true)) == nullptr) {
3277         my_error(ER_UNKNOWN_AUTHID, MYF(0), lex_user->user.str,
3278                  lex_user->host.str);
3279         return true;
3280       }
3281       while ((role = roles_it++) && !errors) {
3282         ACL_USER *acl_role;
3283         if (role->user.length == 0 || *(role->user.str) == '\0') {
3284           /* Anonymous roles aren't allowed */
3285           errors = true;
3286           std::string user_str = create_authid_str_from(acl_user);
3287           std::string role_str = create_authid_str_from(role);
3288           my_error(ER_FAILED_ROLE_GRANT, MYF(0), role_str.c_str(),
3289                    user_str.c_str());
3290           break;
3291         } else if ((acl_role = find_acl_user(role->host.str, role->user.str,
3292                                              true)) == nullptr) {
3293           my_error(ER_UNKNOWN_AUTHID, MYF(0), role->user.str, role->host.str);
3294           errors = true;
3295           break;
3296         } else {
3297           DBUG_PRINT("info", ("User %s@%s will inherit from %s@%s",
3298                               acl_user->user, acl_user->host.get_host(),
3299                               role->user.str, role->host.str));
3300           Security_context *sctx = thd->security_context();
3301           if (sctx->can_operate_with({role}, consts::system_user)) {
3302             errors = true;
3303             break;
3304           }
3305           grant_role(acl_role, acl_user, with_admin_opt);
3306           Auth_id_ref from_user = create_authid_from(role);
3307           Auth_id_ref to_user = create_authid_from(acl_user);
3308           errors = modify_role_edges_in_table(thd, table, from_user, to_user,
3309                                               with_admin_opt, false);
3310           if (errors) {
3311             std::string user_str = create_authid_str_from(acl_user);
3312             std::string role_str = create_authid_str_from(role);
3313             my_error(ER_FAILED_ROLE_GRANT, MYF(0), user_str.c_str(),
3314                      role_str.c_str());
3315             break;
3316           }
3317         }
3318       }
3319     }
3320 
3321     DBUG_ASSERT(!errors || thd->is_error());
3322 
3323     errors = log_and_commit_acl_ddl(thd, transactional_tables);
3324     get_global_acl_cache()->increase_version();
3325   } /* Critical section */
3326 
3327   if (!errors) {
3328     my_ok(thd);
3329     /* Notify storage engines */
3330     acl_notify_htons(thd, SQLCOM_GRANT, users);
3331   }
3332 
3333   return errors;
3334 }
3335 
mysql_grant(THD * thd,const char * db,List<LEX_USER> & list,ulong rights,bool revoke_grant,bool is_proxy,const List<LEX_CSTRING> & dynamic_privilege,bool grant_all_current_privileges,LEX_GRANT_AS * grant_as)3336 bool mysql_grant(THD *thd, const char *db, List<LEX_USER> &list, ulong rights,
3337                  bool revoke_grant, bool is_proxy,
3338                  const List<LEX_CSTRING> &dynamic_privilege,
3339                  bool grant_all_current_privileges, LEX_GRANT_AS *grant_as) {
3340   List_iterator<LEX_USER> str_list(list);
3341   LEX_USER *user, *target_user, *proxied_user = nullptr;
3342   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
3343   bool transactional_tables;
3344   acl_table::Pod_user_what_to_update what_to_set;
3345   bool error = false;
3346   int ret;
3347   TABLE *dynpriv_table;
3348   std::set<LEX_USER *> existing_users;
3349   bool partial_revokes = false;
3350   const List<LEX_CSTRING> *granted_dynamic_privs = &dynamic_privilege;
3351   DBUG_TRACE;
3352   DBUG_ASSERT(initialized);
3353 
3354   if (is_proxy) {
3355     DBUG_ASSERT(!db);
3356     proxied_user = str_list++;
3357   }
3358 
3359   /*
3360     This statement will be replicated as a statement, even when using
3361     row-based replication.  The binlog state will be cleared here to
3362     statement based replication and will be reset to the originals
3363     values when we are out of this function scope
3364   */
3365   Save_and_Restore_binlog_format_state binlog_format_state(thd);
3366   if ((ret = open_grant_tables(thd, tables, &transactional_tables)))
3367     return ret != 1;
3368 
3369   { /* Critical section */
3370     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
3371     if (!acl_cache_lock.lock()) {
3372       commit_and_close_mysql_tables(thd);
3373       return true;
3374     }
3375 
3376     bool with_grant_option = ((rights & GRANT_ACL) != 0);
3377     bool grant_option = thd->lex->grant_privilege;
3378     if (db == nullptr && with_grant_option && (rights & ~GRANT_ACL) == 0 &&
3379         dynamic_privilege.elements > 0) {
3380       /*
3381         If this is a grant on global privilege level and there only dynamic
3382         privileges specified; don't apply the GRANT OPTION on a global privilege
3383         level.
3384       */
3385       rights = 0;
3386     }
3387 
3388     dynpriv_table = tables[ACL_TABLES::TABLE_DYNAMIC_PRIV].table;
3389     Grant_validator grant_validator(
3390         thd, db, list, rights, revoke_grant, dynamic_privilege,
3391         grant_all_current_privileges, grant_as, dynpriv_table);
3392     if (grant_validator.validate()) {
3393       commit_and_close_mysql_tables(thd);
3394       return true;
3395     }
3396 
3397     /* go through users in user_list */
3398     grant_version++;
3399     while ((target_user = str_list++)) {
3400       if (!(user = get_current_user(thd, target_user))) {
3401         error = true;
3402         continue;
3403       }
3404 
3405       Userhostpassword_list password_list;
3406       if (set_and_validate_user_attributes(
3407               thd, user, what_to_set, false, false,
3408               &tables[ACL_TABLES::TABLE_PASSWORD_HISTORY], nullptr,
3409               revoke_grant ? "REVOKE" : "GRANT", password_list)) {
3410         error = true;
3411         continue;
3412       }
3413 
3414       ACL_USER *this_user = find_acl_user(user->host.str, user->user.str, true);
3415       Restrictions restrictions(nullptr);
3416       DB_restrictions db_restrictions(nullptr);
3417       ulong filtered_rights = rights;
3418       std::unique_ptr<Restrictions_aggregator> aggregator =
3419           Restrictions_aggregator_factory::create(thd, this_user, db, rights,
3420                                                   grant_all_current_privileges);
3421       if (aggregator) {
3422         partial_revokes = true;
3423         if (aggregator->generate(db_restrictions)) {
3424           error = true;
3425           break;
3426         }
3427         restrictions.set_db(db_restrictions);
3428         what_to_set.m_what |= USER_ATTRIBUTES;
3429         what_to_set.m_user_attributes |= acl_table::USER_ATTRIBUTE_RESTRICTIONS;
3430       }
3431       if (this_user && (what_to_set.m_what & PLUGIN_ATTR))
3432         existing_users.insert(target_user);
3433       what_to_set.m_what |= ACCESS_RIGHTS_ATTR;
3434       if ((ret = replace_user_table(thd, tables[ACL_TABLES::TABLE_USER].table,
3435                                     user, (!db ? rights : 0), revoke_grant,
3436                                     false, what_to_set, &restrictions))) {
3437         error = true;
3438         if (ret < 0) break;
3439 
3440         continue;
3441       }
3442       /*
3443          DB table operation is needed in either of the following cases :
3444           - There is no partial revoke(s)
3445           - Hybrid operation; Partial revokes filtered some of the access
3446             for DB table
3447       */
3448       else if (db && (aggregator == nullptr ||
3449                       aggregator->find_if_require_next_level_operation(
3450                           filtered_rights))) {
3451         ulong db_rights = filtered_rights & DB_ACLS;
3452         if (db_rights == filtered_rights) {
3453           if ((ret = replace_db_table(thd, tables[ACL_TABLES::TABLE_DB].table,
3454                                       db, *user, db_rights, revoke_grant))) {
3455             error = true;
3456             if (ret < 0) break;
3457 
3458             continue;
3459           }
3460           thd->add_to_binlog_accessed_dbs(db);
3461         } else {
3462           my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
3463           error = true;
3464           continue;
3465         }
3466       } else if (is_proxy) {
3467         if ((ret = replace_proxies_priv_table(
3468                  thd, tables[5].table, user, proxied_user,
3469                  rights & GRANT_ACL ? true : false, revoke_grant))) {
3470           error = true;
3471           if (ret < 0) break;
3472 
3473           continue;
3474         }
3475       }
3476 
3477       if (!db &&
3478           (dynamic_privilege.elements > 0 || grant_all_current_privileges)) {
3479         LEX_CSTRING *priv;
3480         Update_dynamic_privilege_table update_table(thd, dynpriv_table);
3481         List<LEX_CSTRING> *privileges_to_check;
3482         if (grant_all_current_privileges) {
3483           /*
3484             Copy all currently available dynamic privileges to the list of
3485             dynamic privileges to grant.
3486           */
3487           privileges_to_check = new (thd->mem_root) List<LEX_CSTRING>;
3488           iterate_all_dynamic_privileges(thd, [&](const char *str) {
3489             LEX_CSTRING *new_str =
3490                 (LEX_CSTRING *)thd->alloc(sizeof(LEX_CSTRING));
3491             new_str->str = str;
3492             new_str->length = strlen(str);
3493             privileges_to_check->push_back(new_str);
3494             return false;
3495           });
3496           granted_dynamic_privs = privileges_to_check;
3497         } else
3498           privileges_to_check =
3499               &const_cast<List<LEX_CSTRING> &>(dynamic_privilege);
3500         List_iterator<LEX_CSTRING> priv_it(*privileges_to_check);
3501         while ((priv = priv_it++) && !error) {
3502           /* We already checked privileges required to perform GRANT/REVOKE */
3503           if (revoke_grant) {
3504             error = revoke_dynamic_privilege(*priv, user->user, user->host,
3505                                              update_table);
3506           } else {
3507             /* We performed SYSTEM_USER check before */
3508             error = grant_dynamic_privilege(*priv, user->user, user->host,
3509                                             with_grant_option, update_table);
3510           }
3511           if (error) {
3512             /*
3513               If the operation fails the DA might have been set already, but
3514               if wasn't we can assume the dynamic privilege wasn't
3515               registered in which case a syntax error is a reasonable response.
3516             */
3517             if (!thd->get_stmt_da()->is_error())
3518               my_error(ER_SYNTAX_ERROR, MYF(0));
3519             break;
3520           }
3521         }
3522       }
3523       if (!db && grant_option) {
3524         bool dynamic_privileges_error = false;
3525         Update_dynamic_privilege_table update_table(thd, dynpriv_table);
3526         if (!revoke_grant)
3527           dynamic_privileges_error =
3528               grant_grant_option_for_all_dynamic_privileges(
3529                   user->user, user->host, update_table);
3530         else
3531           dynamic_privileges_error =
3532               revoke_grant_option_for_all_dynamic_privileges(
3533                   user->user, user->host, update_table);
3534         if (dynamic_privileges_error) {
3535           if (!thd->get_stmt_da()->is_error())
3536             my_error(ER_SYNTAX_ERROR, MYF(0));
3537         }
3538       }
3539     }  // for each user
3540 
3541     DBUG_ASSERT(!error || thd->is_error());
3542 
3543     {
3544       /*
3545         We want to add AS ... clause while rewritting GRANT statement in
3546         following cases:
3547 
3548         1. The GRANT statement being executed contains AS clause.
3549            In this case we retain it as it is except rewriting
3550            CURRENT_USER() and use current session's user/host part.
3551         2. If all of the following condtions are met:
3552            - --partial_revokes is ON
3553            - Statement is a GRANT at global level (*.*)
3554            This is required because, current user may have restriction
3555            list and grant may propagate the same to grantee. In order
3556            to repliably replay this on other nodes in an HA setup, details
3557            of current user has to be captured.
3558       */
3559       LEX_GRANT_AS *grant_as_ptr = nullptr;
3560       bool grant_as_specified = false;
3561       LEX_GRANT_AS grant_as_for_rewrite;
3562       if (grant_as->grant_as_used) {
3563         grant_as_specified = grant_as->grant_as_used;
3564         grant_as_ptr = grant_as;
3565       } else if (partial_revokes && !revoke_grant && !is_proxy && !db) {
3566         /* Set LEX_GRANT_AS for given GRANT */
3567         grant_as_for_rewrite.grant_as_used = true;
3568 
3569         grant_as_for_rewrite.role_type =
3570             thd->security_context()->get_active_roles()->size()
3571                 ? role_enum::ROLE_NAME
3572                 : role_enum::ROLE_NONE;
3573 
3574         LEX_CSTRING priv_user = thd->security_context()->priv_user();
3575         LEX_CSTRING priv_host = thd->security_context()->priv_host();
3576         grant_as_for_rewrite.user = LEX_USER::alloc(
3577             thd, (LEX_STRING *)&priv_user, (LEX_STRING *)&priv_host);
3578 
3579         if (grant_as_for_rewrite.role_type == role_enum::ROLE_NAME) {
3580           grant_as_for_rewrite.role_list = new (thd->mem_root) List<LEX_USER>;
3581           thd->security_context()->get_active_roles(
3582               thd, *grant_as_for_rewrite.role_list);
3583         }
3584         grant_as_specified = false;
3585         grant_as_ptr = &grant_as_for_rewrite;
3586       }
3587 
3588       Grant_params grant_rewrite_params(grant_as_specified, grant_as_ptr);
3589 
3590       error = log_and_commit_acl_ddl(thd, transactional_tables, nullptr,
3591                                      &grant_rewrite_params);
3592     }
3593 
3594     {
3595       /* Notify audit plugin. We will ignore the return value. */
3596       LEX_USER *existing_user;
3597       for (LEX_USER *one_user : existing_users) {
3598         if ((existing_user = get_current_user(thd, one_user)))
3599           mysql_audit_notify(
3600               thd, AUDIT_EVENT(MYSQL_AUDIT_AUTHENTICATION_CREDENTIAL_CHANGE),
3601               thd->is_error(), existing_user->user.str, existing_user->host.str,
3602               existing_user->plugin.str, is_role_id(existing_user), nullptr,
3603               nullptr);
3604       }
3605     }
3606 
3607     get_global_acl_cache()->increase_version();
3608   } /* Critical section */
3609 
3610   if (!error) {
3611     my_ok(thd);
3612     /* Notify storage engines */
3613     acl_notify_htons(thd, revoke_grant ? SQLCOM_REVOKE : SQLCOM_GRANT, &list,
3614                      nullptr, granted_dynamic_privs);
3615   }
3616 
3617   return error;
3618 }
3619 
3620 /**
3621   @brief Check table level grants
3622 
3623   @param thd          Thread handler
3624   @param want_access  Bits of privileges user needs to have.
3625   @param tables       List of tables to check. The user should have
3626                       'want_access' to all tables in list.
3627   @param any_combination_will_do true if it's enough to have any privilege for
3628     any combination of the table columns.
3629   @param number       Check at most this number of tables.
3630   @param no_errors    true if no error should be sent directly to the client.
3631 
3632   If table->grant.want_privilege != 0 then the requested privileges where
3633   in the set of COL_ACLS but access was not granted on the table level. As
3634   a consequence an extra check of column privileges is required.
3635 
3636   Specifically if this function returns false the user has some kind of
3637   privilege on a combination of columns in each table.
3638 
3639   This function is usually preceeded by check_access which establish the
3640   User-, Db- and Host access rights.
3641 
3642   @see check_access
3643   @see check_table_access
3644 
3645   @note This functions assumes that either number of tables to be inspected
3646      by it is limited explicitly (i.e. is is not UINT_MAX) or table list
3647      used and thd->lex->query_tables_own_last value correspond to each
3648      other (the latter should be either 0 or point to next_global member
3649      of one of elements of this table list).
3650 
3651    @return Access status
3652      @retval false Access granted; But column privileges need to be checked.
3653      @retval true The user did not have the requested privileges on any of the
3654       tables.
3655 
3656 */
3657 
check_grant(THD * thd,ulong want_access,TABLE_LIST * tables,bool any_combination_will_do,uint number,bool no_errors)3658 bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
3659                  bool any_combination_will_do, uint number, bool no_errors) {
3660   TABLE_LIST *tl;
3661   TABLE_LIST *const first_not_own_table = thd->lex->first_not_own_table();
3662   Security_context *sctx = thd->security_context();
3663   ulong orig_want_access = want_access;
3664   std::vector<TABLE_LIST *> tables_to_be_processed_further;
3665   DBUG_TRACE;
3666   DBUG_ASSERT(number > 0);
3667 
3668   for (tl = tables; tl && number-- && tl != first_not_own_table;
3669        tl = tl->next_global) {
3670     TABLE_LIST *const t_ref =
3671         tl->correspondent_table ? tl->correspondent_table : tl;
3672     sctx = (t_ref->security_ctx != nullptr) ? t_ref->security_ctx
3673                                             : thd->security_context();
3674     const char *db_name = t_ref->get_db_name();
3675     const ACL_internal_table_access *access = get_cached_table_access(
3676         &t_ref->grant.m_internal, db_name, t_ref->get_table_name());
3677 
3678     if (access) {
3679       switch (access->check(orig_want_access, &t_ref->grant.privilege)) {
3680         case ACL_INTERNAL_ACCESS_GRANTED:
3681           /*
3682              Grant all access to the table to skip column checks.
3683              Depend on the controls in the P_S table itself.
3684           */
3685           t_ref->grant.privilege |= TMP_TABLE_ACLS;
3686           continue;
3687         case ACL_INTERNAL_ACCESS_DENIED:
3688           goto err;
3689         case ACL_INTERNAL_ACCESS_CHECK_GRANT:
3690           break;
3691       }
3692     }
3693 
3694     want_access = orig_want_access;
3695     want_access &= ~sctx->master_access(db_name);
3696     if (!want_access) continue;  // ok
3697 
3698     if (!(~t_ref->grant.privilege & want_access) || t_ref->is_internal() ||
3699         t_ref->schema_table)
3700       continue;
3701 
3702     if (is_temporary_table(t_ref)) {
3703       /*
3704         If this table list element corresponds to a pre-opened temporary
3705         table skip checking of all relevant table-level privileges for it.
3706         Note that during creation of temporary table we still need to check
3707         if user has CREATE_TMP_ACL.
3708       */
3709       t_ref->grant.privilege |= TMP_TABLE_ACLS;
3710       continue;
3711     }
3712 
3713     if (sctx->get_active_roles()->size() != 0) {
3714       t_ref->grant.grant_table = nullptr;
3715       t_ref->grant.version = grant_version;
3716 
3717       Grant_table_aggregate aggr = sctx->table_and_column_acls(
3718           {t_ref->get_db_name(), strlen(t_ref->get_db_name())},
3719           {t_ref->get_table_name(), strlen(t_ref->get_table_name())});
3720 
3721       DBUG_PRINT("info",
3722                  ("Acl_map table %s.%s has access %lu", t_ref->get_db_name(),
3723                   t_ref->get_table_name(), aggr.table_access));
3724       /*
3725         For SHOW COLUMNS, SHOW INDEX it is enough to have some
3726         privileges on any column combination on the table.
3727       */
3728       if (any_combination_will_do && (aggr.cols != 0 || aggr.table_access != 0))
3729         continue;
3730 
3731       /*
3732         For SHOW COLUMNS, SHOW INDEX it is enough to have some
3733         privileges on any column combination on the table.
3734       */
3735       if (any_combination_will_do) continue;
3736       t_ref->grant.privilege = aggr.table_access;
3737       if (!(~t_ref->grant.privilege & want_access)) {
3738         DBUG_PRINT("info",
3739                    ("Access not denied because of column acls for %s.%s."
3740                     "want_access= %lu, grant.privilege= %lu",
3741                     t_ref->get_db_name(), t_ref->get_table_name(), want_access,
3742                     t_ref->grant.privilege));
3743         continue;
3744       }
3745       if (want_access & ~(aggr.cols | t_ref->grant.privilege)) {
3746         want_access &= ~(aggr.cols | t_ref->grant.privilege);
3747         DBUG_PRINT("info", ("Access denied for %s.%s. Unfulfilled access: %lu",
3748                             t_ref->get_db_name(), t_ref->get_table_name(),
3749                             want_access));
3750         goto err;
3751       }
3752     } else {
3753       /*
3754         For these tables we have to access ACL caches.
3755         We will first go over all TABLE_LIST objects and
3756         create a list of objects to be processed further.
3757         Later, we will access ACL caches for these tables.
3758         It allows us to delay locking of ACL caches.
3759       */
3760       tables_to_be_processed_further.push_back(tl);
3761     }  // end else
3762   }    // end for
3763 
3764   if (!tables_to_be_processed_further.empty()) {
3765     tl = nullptr;
3766     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
3767     if (!acl_cache_lock.lock(!no_errors)) return true;
3768 
3769     for (TABLE_LIST *tl_tmp : tables_to_be_processed_further) {
3770       tl = tl_tmp;
3771       TABLE_LIST *const t_ref =
3772           tl->correspondent_table ? tl->correspondent_table : tl;
3773       sctx = (t_ref->security_ctx != nullptr) ? t_ref->security_ctx
3774                                               : thd->security_context();
3775       const char *db_name = t_ref->get_db_name();
3776       want_access = orig_want_access;
3777       want_access &= ~sctx->master_access(db_name);
3778       DBUG_ASSERT(want_access != 0);
3779 
3780       GRANT_TABLE *grant_table = table_hash_search(
3781           sctx->host().str, sctx->ip().str, db_name, sctx->priv_user().str,
3782           t_ref->get_table_name(), false);
3783 
3784       if (!grant_table) {
3785         DBUG_PRINT("info",
3786                    ("Table %s didn't exist in the legacy table acl cache",
3787                     t_ref->get_table_name()));
3788         want_access &= ~t_ref->grant.privilege;
3789         goto err;  // No grants
3790       }
3791 
3792       /*
3793         For SHOW COLUMNS, SHOW INDEX it is enough to have some
3794         privileges on any column combination on the table.
3795       */
3796       if (any_combination_will_do) continue;
3797 
3798       t_ref->grant.grant_table = grant_table;  // Remember for column test
3799       t_ref->grant.version = grant_version;
3800       t_ref->grant.privilege |= grant_table->privs;
3801 
3802       DBUG_PRINT("info",
3803                  ("t_ref->grant.privilege = %lu", t_ref->grant.privilege));
3804       if (!(~t_ref->grant.privilege & want_access)) continue;
3805 
3806       if (want_access & ~(grant_table->cols | t_ref->grant.privilege)) {
3807         want_access &= ~(grant_table->cols | t_ref->grant.privilege);
3808         goto err;  // impossible
3809       }
3810     }
3811   }
3812   return false;
3813 
3814 err:
3815 
3816   if (!no_errors)  // Not a silent skip of table
3817   {
3818     char command[128];
3819     get_privilege_desc(command, sizeof(command), want_access);
3820     my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), command,
3821              sctx->priv_user().str, sctx->host_or_ip().str,
3822              tl ? tl->get_table_name() : "unknown");
3823   }
3824   return true;
3825 }
3826 
3827 /*
3828   Check column rights in given security context
3829 
3830   SYNOPSIS
3831     check_grant_column()
3832     thd                  thread handler
3833     grant                grant information structure
3834     db_name              db name
3835     table_name           table  name
3836     name                 column name
3837     length               column name length
3838     sctx                 security context
3839     want_privilege       wanted privileges
3840 
3841   RETURN
3842     false OK
3843     true  access denied
3844 */
3845 
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)3846 bool check_grant_column(THD *thd, GRANT_INFO *grant, const char *db_name,
3847                         const char *table_name, const char *name, size_t length,
3848                         Security_context *sctx, ulong want_privilege) {
3849   GRANT_TABLE *grant_table;
3850   GRANT_COLUMN *grant_column;
3851   DBUG_TRACE;
3852   DBUG_PRINT("enter",
3853              ("table: %s  want_privilege: %lu", table_name, want_privilege));
3854 
3855   // Adjust wanted privileges based on privileges granted to table:
3856   want_privilege &= ~grant->privilege;
3857   if (!want_privilege) return false;  // Already checked
3858   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
3859   if (!acl_cache_lock.lock()) return true;
3860 
3861   /* reload table if someone has modified any grants */
3862   if (sctx->get_active_roles()->size() != 0) {
3863     DBUG_ASSERT(grant->grant_table == nullptr);
3864     Grant_table_aggregate agg = sctx->table_and_column_acls(
3865         {(const char *)db_name, strlen(db_name)},
3866         {(const char *)table_name, strlen(table_name)});
3867     std::string q_name(name);
3868     Column_map::iterator it = agg.columns.find(q_name);
3869     if (it != agg.columns.end()) {
3870       if (!(~(it->second) & want_privilege)) {
3871         DBUG_PRINT("info", ("Sufficient column privileges found for %s.%s.%s",
3872                             db_name, table_name, name));
3873         return false;
3874       }
3875     } else {
3876       DBUG_PRINT("info", ("No column privileges found for %s.%s.%s", db_name,
3877                           table_name, name));
3878     }
3879   } else {
3880     if (grant->version != grant_version) {
3881       grant->grant_table = table_hash_search(
3882           sctx->host().str, sctx->ip().str, db_name, sctx->priv_user().str,
3883           table_name, false);         /* purecov: inspected */
3884       grant->version = grant_version; /* purecov: inspected */
3885     }
3886     if (!(grant_table = grant->grant_table)) goto err; /* purecov: deadcode */
3887 
3888     grant_column = column_hash_search(grant_table, name, length);
3889     if (grant_column && !(~grant_column->rights & want_privilege)) {
3890       return false;
3891     }
3892   }
3893 
3894 err:
3895   char command[128];
3896   get_privilege_desc(command, sizeof(command), want_privilege);
3897   my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), command, sctx->priv_user().str,
3898            sctx->host_or_ip().str, name, table_name);
3899   return true;
3900 }
3901 
3902 /**
3903   Check the privileges for a column depending on the type of table.
3904 
3905   @param thd              thread handler
3906   @param table_ref        table reference where to check the field
3907   @param name             name of field to check
3908   @param length           length of name
3909   @param want_privilege   wanted privileges
3910 
3911   Check the privileges for a column depending on the type of table the column
3912   belongs to. The function provides a generic interface to check column
3913   privileges that hides the heterogeneity of the column representation -
3914   whether it belongs to a view or a base table.
3915 
3916   Notice that this function does not understand that a column from a view
3917   reference must be checked for privileges both in the view and in the
3918   underlying base table (or view) reference. This is the responsibility of
3919   the caller.
3920 
3921   Columns from temporary tables and derived tables are ignored by this function.
3922 
3923   @returns false if success, true if error (access denied)
3924 */
3925 
check_column_grant_in_table_ref(THD * thd,TABLE_LIST * table_ref,const char * name,size_t length,ulong want_privilege)3926 bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST *table_ref,
3927                                      const char *name, size_t length,
3928                                      ulong want_privilege) {
3929   DBUG_TRACE;
3930   GRANT_INFO *grant;
3931   const char *db_name;
3932   const char *table_name;
3933   Security_context *sctx = (table_ref->security_ctx != nullptr)
3934                                ? table_ref->security_ctx
3935                                : thd->security_context();
3936 
3937   DBUG_ASSERT(want_privilege);
3938 
3939   if (is_temporary_table(table_ref) || table_ref->is_internal()) {
3940     // Temporary table or optimizer internal table: no need to evaluate
3941     // privileges
3942     return false;
3943   } else if (table_ref->is_view() || table_ref->field_translation) {
3944     /* View or derived information schema table. */
3945     ulong view_privs;
3946     grant = &(table_ref->grant);
3947     db_name = table_ref->view_db.str;
3948     table_name = table_ref->view_name.str;
3949     if (table_ref->belong_to_view &&
3950         thd->lex->sql_command == SQLCOM_SHOW_FIELDS) {
3951       if (sctx->get_active_roles()->size() > 0) {
3952         view_privs = sctx->table_acl({db_name, strlen(db_name)},
3953                                      {table_name, strlen(table_name)});
3954         DBUG_PRINT("info", ("Found role privileges for %s.%s : %lu", db_name,
3955                             table_name, view_privs));
3956       } else
3957         view_privs = get_column_grant(thd, grant, db_name, table_name, name);
3958       if (view_privs & VIEW_ANY_ACL) {
3959         return false;
3960       }
3961       my_error(ER_VIEW_NO_EXPLAIN, MYF(0));
3962       return true;
3963     }
3964   } else if (table_ref->nested_join) {
3965     for (TABLE_LIST *table : table_ref->nested_join->join_list) {
3966       if (check_column_grant_in_table_ref(thd, table, name, length,
3967                                           want_privilege)) {
3968         return true;
3969       }
3970     }
3971     return false;
3972   } else {
3973     // Regular, persistent base table
3974     grant = &table_ref->grant;
3975     db_name = table_ref->db;
3976     table_name = table_ref->table_name;
3977     DBUG_ASSERT(strcmp(db_name, table_ref->table->s->db.str) == 0 &&
3978                 strcmp(table_name, table_ref->table->s->table_name.str) == 0);
3979   }
3980 
3981   if (check_grant_column(thd, grant, db_name, table_name, name, length, sctx,
3982                          want_privilege))
3983     return true;
3984   return false;
3985 }
3986 
3987 /**
3988   @brief check if a query can access a set of columns
3989 
3990   @param  thd  the current thread
3991   @param  want_access_arg  the privileges requested
3992   @param  fields an iterator over the fields of a table reference.
3993   @return Operation status
3994     @retval 0 Success
3995     @retval 1 Falure
3996   @details This function walks over the columns of a table reference
3997    The columns may originate from different tables, depending on the kind of
3998    table reference, e.g. join, view.
3999    For each table it will retrieve the grant information and will use it
4000    to check the required access privileges for the fields requested from it.
4001 */
check_grant_all_columns(THD * thd,ulong want_access_arg,Field_iterator_table_ref * fields)4002 bool check_grant_all_columns(THD *thd, ulong want_access_arg,
4003                              Field_iterator_table_ref *fields) {
4004   Security_context *sctx = thd->security_context();
4005   ulong want_access = want_access_arg;
4006   const char *table_name = nullptr;
4007   const char *field_name = nullptr;
4008   const char *db_name = nullptr;
4009   GRANT_INFO *grant = nullptr;
4010   GRANT_TABLE *grant_table = nullptr;
4011   /*
4012      Flag that gets set if privilege checking has to be performed on column
4013      level.
4014   */
4015   bool using_column_privileges = false;
4016   bool has_roles = thd->security_context()->get_active_roles()->size() > 0;
4017   Grant_table_aggregate aggr;
4018   DEBUG_SYNC(thd, "in_check_grant_all_columns");
4019   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
4020   if (!acl_cache_lock.lock()) return true;
4021 
4022   for (; !fields->end_of_fields(); fields->next()) {
4023     grant = fields->grant(); /* Get cached GRANT_INFO on field */
4024     // Check the privileges at column level if table does not have wanted access
4025     want_access = want_access_arg & ~grant->privilege;
4026     if (want_access) {
4027       field_name = fields->name();
4028       table_name = fields->get_table_name();
4029       db_name = fields->get_db_name();
4030       if (has_roles) {
4031         LEX_CSTRING str_db_name = {db_name, strlen(db_name)};
4032         LEX_CSTRING str_table_name = {table_name, strlen(table_name)};
4033         aggr = thd->security_context()->table_and_column_acls(str_db_name,
4034                                                               str_table_name);
4035         /* Update it to reflect current role privileges */
4036         grant->privilege = aggr.table_access;
4037         /* Reduce remaining privilege requirements */
4038         want_access = want_access_arg & ~grant->privilege;
4039         /* Does any of the columns have the access we need? */
4040         if (aggr.cols & want_access) {
4041           /* Find our column */
4042           std::string q_name(field_name);
4043           Column_map::iterator it = aggr.columns.find(q_name);
4044           if (it != aggr.columns.end()) using_column_privileges = true;
4045           if (it == aggr.columns.end() || (~(it->second) & want_access)) {
4046             DBUG_PRINT("info", ("No column privileges found for %s.%s.%s",
4047                                 db_name, table_name, field_name));
4048             goto err;
4049           }
4050           DBUG_PRINT("info", ("Sufficient column privileges found for %s.%s.%s",
4051                               db_name, table_name, field_name));
4052         }
4053       } else {
4054         /* reload table if someone has modified any grants */
4055         if (grant->version != grant_version) {
4056           grant->grant_table = table_hash_search(
4057               sctx->host().str, sctx->ip().str, db_name, sctx->priv_user().str,
4058               table_name, false);         /* purecov: inspected */
4059           grant->version = grant_version; /* purecov: inspected */
4060         }
4061         grant_table = grant->grant_table;
4062         if (grant_table == nullptr) goto err;
4063         GRANT_COLUMN *grant_column =
4064             column_hash_search(grant_table, field_name, strlen(field_name));
4065         if (grant_column) using_column_privileges = true;
4066         if (grant_column == nullptr || (~grant_column->rights & want_access))
4067           goto err;
4068       }
4069     }
4070   }  // next field
4071   return false;
4072 
4073 err:
4074 
4075   char command[128];
4076   get_privilege_desc(command, sizeof(command), want_access);
4077   /*
4078     Do not give an error message listing a column name unless the user has
4079     privilege to see all columns.
4080   */
4081   if (using_column_privileges)
4082     my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), command,
4083              sctx->priv_user().str, sctx->host_or_ip().str, table_name);
4084   else
4085     my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), command,
4086              sctx->priv_user().str, sctx->host_or_ip().str, fields->name(),
4087              table_name);
4088   return true;
4089 }
4090 
check_grant_db_routine(THD * thd,const char * db,malloc_unordered_multimap<std::string,unique_ptr_destroy_only<GRANT_NAME>> * hash)4091 static bool check_grant_db_routine(
4092     THD *thd, const char *db,
4093     malloc_unordered_multimap<std::string, unique_ptr_destroy_only<GRANT_NAME>>
4094         *hash) {
4095   DBUG_TRACE;
4096   Security_context *sctx = thd->security_context();
4097   DBUG_ASSERT(assert_acl_cache_read_lock(thd));
4098 
4099   if (sctx->get_active_roles()->size() != 0 && db != nullptr) {
4100     DBUG_PRINT("info",
4101                ("Using roles Acl_map to detect schema level privileges"));
4102     ulong acl = sctx->db_acl({db, strlen(db)});
4103     return acl == 0;
4104   } else {
4105     for (const auto &key_and_value : *hash) {
4106       GRANT_NAME *item = key_and_value.second.get();
4107 
4108       if (strcmp(item->user, sctx->priv_user().str) == 0 &&
4109           strcmp(item->db, db) == 0 &&
4110           item->host.compare_hostname(sctx->host().str, sctx->ip().str)) {
4111         return false;
4112       }
4113     }  // end for
4114   }
4115 
4116   return true;
4117 }
4118 
has_any_table_acl(Security_context * sctx,const LEX_CSTRING & str)4119 bool has_any_table_acl(Security_context *sctx, const LEX_CSTRING &str) {
4120   if (sctx->get_active_roles()->size() > 0) {
4121     return sctx->any_table_acl(str);
4122   }
4123   return false;
4124 }
4125 
has_any_routine_acl(Security_context * sctx,const LEX_CSTRING & db)4126 bool has_any_routine_acl(Security_context *sctx, const LEX_CSTRING &db) {
4127   if (sctx->get_active_roles()->size() > 0) {
4128     return sctx->any_sp_acl(db);
4129   }
4130   return false;
4131 }
4132 
4133 /**
4134   Check if a user has the right to access a database.
4135   Access is accepted if the user has a database operations related grant
4136   (i.e. not including the GRANT_ACL) for any table/column/routine in the
4137   database.
4138 
4139   @param thd The thread handler
4140   @param db The name of the database
4141 
4142   @retval 1 Access is denied
4143   @retval 0 Otherwise
4144 */
check_grant_db(THD * thd,const char * db)4145 bool check_grant_db(THD *thd, const char *db) {
4146   DBUG_TRACE;
4147   Security_context *sctx = thd->security_context();
4148   LEX_CSTRING priv_user = sctx->priv_user();
4149   bool error = true;
4150 
4151   if (sctx->get_active_roles()->size() > 0) {
4152     size_t db_len = strlen(db);
4153     ulong db_access = sctx->db_acl({db, db_len});
4154     if ((db_access & DB_OP_ACLS) != 0) return false;
4155     return !has_any_table_acl(sctx, {db, db_len}) &&
4156            !has_any_routine_acl(sctx, {db, db_len});
4157   }
4158 
4159   std::string key = to_string(priv_user);
4160   key.push_back('\0');
4161   key.append(db);
4162   key.push_back('\0');
4163 
4164   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
4165   if (!acl_cache_lock.lock()) return true;
4166 
4167   for (const auto &key_and_value : *column_priv_hash) {
4168     GRANT_TABLE *grant_table = key_and_value.second.get();
4169     if (grant_table->hash_key.compare(0, key.size(), key) == 0 &&
4170         grant_table->host.compare_hostname(sctx->host().str, sctx->ip().str) &&
4171         ((grant_table->privs | grant_table->cols) & TABLE_OP_ACLS)) {
4172       error = false; /* Found match. */
4173       DBUG_PRINT("info", ("Detected table level acl in column_priv_hash"));
4174       break;
4175     }
4176   }
4177 
4178   if (error) {
4179     DBUG_PRINT("info", ("No table level acl in column_priv_hash; checking "
4180                         "for schema level acls"));
4181     error = check_grant_db_routine(thd, db, proc_priv_hash.get()) &&
4182             check_grant_db_routine(thd, db, func_priv_hash.get());
4183   }
4184 
4185   return error;
4186 }
4187 
4188 /****************************************************************************
4189   Check routine level grants
4190 
4191   SYNPOSIS
4192    bool check_grant_routine()
4193    thd          Thread handler
4194    want_access  Bits of privileges user needs to have
4195    procs        List of routines to check. The user should have 'want_access'
4196    is_proc      True if the list is all procedures, else functions
4197    no_errors    If 0 then we write an error. The error is sent directly to
4198                 the client
4199 
4200    RETURN
4201      false  ok
4202      true  Error: User did not have the requested privielges
4203 ****************************************************************************/
4204 
check_grant_routine(THD * thd,ulong want_access,TABLE_LIST * procs,bool is_proc,bool no_errors)4205 bool check_grant_routine(THD *thd, ulong want_access, TABLE_LIST *procs,
4206                          bool is_proc, bool no_errors) {
4207   TABLE_LIST *table;
4208   Security_context *sctx = thd->security_context();
4209   const char *user = sctx->priv_user().str;
4210   const char *host = sctx->priv_host().str;
4211   const std::string db_name(procs->db, procs->db_length);
4212   bool has_roles = thd->security_context()->get_active_roles()->size() > 0;
4213   DBUG_TRACE;
4214 
4215   want_access &= ~sctx->master_access(db_name);
4216   if (!want_access) return false;  // ok
4217 
4218   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
4219   if (!acl_cache_lock.lock()) return true;
4220 
4221   for (table = procs; table; table = table->next_global) {
4222     if (has_roles) {
4223       ulong acl;
4224       if (is_proc) {
4225         acl =
4226             sctx->procedure_acl({table->db, table->db_length},
4227                                 {table->table_name, table->table_name_length});
4228       } else {
4229         acl = sctx->function_acl({table->db, table->db_length},
4230                                  {table->table_name, table->table_name_length});
4231       }
4232       table->grant.privilege |= acl;
4233       DBUG_PRINT("info",
4234                  ("Checking Acl_map for proc acls in %s.%s; "
4235                   "found %lu",
4236                   table->db, table->table_name, table->grant.privilege));
4237     } else {
4238       GRANT_NAME *grant_proc;
4239       if ((grant_proc =
4240                routine_hash_search(host, sctx->ip().str, table->db, user,
4241                                    table->table_name, is_proc, false))) {
4242         table->grant.privilege |= grant_proc->privs;
4243         DBUG_PRINT("info", ("Checking for routine acls in %s; "
4244                             "found %lu",
4245                             table->db, grant_proc->privs));
4246       }
4247     }
4248     if ((want_access & table->grant.privilege) != want_access) {
4249       want_access &= ~table->grant.privilege;
4250       goto err;
4251     }
4252   }
4253   return false;
4254 
4255 err:
4256   if (!no_errors) {
4257     char buff[1024];
4258     const char *command = "";
4259     if (table) strxmov(buff, table->db, ".", table->table_name, NullS);
4260     if (want_access & EXECUTE_ACL)
4261       command = "execute";
4262     else if (want_access & ALTER_PROC_ACL)
4263       command = "alter routine";
4264     else if (want_access & GRANT_ACL)
4265       command = "grant";
4266     my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0), command, user, host,
4267              table ? buff : "unknown");
4268   }
4269   return true;
4270 }
4271 
4272 /*
4273   Check if routine has any of the
4274   routine level grants
4275 
4276   SYNPOSIS
4277    bool    check_routine_level_acl()
4278    thd          Thread handler
4279    db           Database name
4280    name         Routine name
4281 
4282   RETURN
4283    false           Ok
4284    true            error
4285 */
4286 
check_routine_level_acl(THD * thd,const char * db,const char * name,bool is_proc)4287 static bool check_routine_level_acl(THD *thd, const char *db, const char *name,
4288                                     bool is_proc) {
4289   DBUG_TRACE;
4290   bool no_routine_acl = true;
4291   GRANT_NAME *grant_proc;
4292   Security_context *sctx = thd->security_context();
4293   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
4294   if (!acl_cache_lock.lock(false)) return no_routine_acl;
4295 
4296   if ((grant_proc =
4297            routine_hash_search(sctx->priv_host().str, sctx->ip().str, db,
4298                                sctx->priv_user().str, name, is_proc, false)))
4299     no_routine_acl = !(grant_proc->privs & SHOW_PROC_ACLS);
4300   return no_routine_acl;
4301 }
4302 
4303 /*****************************************************************************
4304   Functions to retrieve the grant for a table/column  (for SHOW functions)
4305 *****************************************************************************/
4306 
get_table_grant(THD * thd,TABLE_LIST * table)4307 ulong get_table_grant(THD *thd, TABLE_LIST *table) {
4308   ulong privilege;
4309   Security_context *sctx = thd->security_context();
4310   const char *db = table->db ? table->db : thd->db().str;
4311   GRANT_TABLE *grant_table;
4312   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
4313 
4314   if (!acl_cache_lock.lock(false)) return (NO_ACCESS);
4315 
4316   grant_table =
4317       table_hash_search(sctx->host().str, sctx->ip().str, db,
4318                         sctx->priv_user().str, table->table_name, false);
4319   table->grant.grant_table = grant_table;  // Remember for column test
4320   table->grant.version = grant_version;
4321   if (grant_table) table->grant.privilege |= grant_table->privs;
4322   privilege = table->grant.privilege;
4323   return privilege;
4324 }
4325 
4326 /*
4327   Determine the access priviliges for a field.
4328 
4329   SYNOPSIS
4330     get_column_grant()
4331     thd         thread handler
4332     grant       grants table descriptor
4333     db_name     name of database that the field belongs to
4334     table_name  name of table that the field belongs to
4335     field_name  name of field
4336 
4337   DESCRIPTION
4338     The procedure may also modify: grant->grant_table and grant->version.
4339 
4340   RETURN
4341     The access priviliges for the field db_name.table_name.field_name
4342 */
4343 
get_column_grant(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * field_name)4344 ulong get_column_grant(THD *thd, GRANT_INFO *grant, const char *db_name,
4345                        const char *table_name, const char *field_name) {
4346   GRANT_TABLE *grant_table;
4347   GRANT_COLUMN *grant_column;
4348   ulong priv;
4349   Security_context *sctx = thd->security_context();
4350   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
4351 
4352   if (!acl_cache_lock.lock(false)) return (NO_ACCESS);
4353 
4354   /* reload table if someone has modified any grants */
4355   /*
4356     note: grant->grant_table is set iff we need to check column level routines
4357     and table level privileges hasn't isn't enough to fulfill the requirement.
4358     However, we might be using this routine to check table level privileges
4359     too so we must still check these before we continue. Doh..
4360   */
4361   if (sctx->get_active_roles()->size() != 0 && !grant->grant_table) {
4362     priv = grant->privilege;
4363     Grant_table_aggregate aggr = sctx->table_and_column_acls(
4364         {(const char *)db_name, strlen(db_name)},
4365         {(const char *)table_name, strlen(table_name)});
4366     priv |= aggr.table_access;
4367     /* Find our column */
4368     std::string q_name;
4369     q_name.append(field_name);
4370     Column_map::iterator it = aggr.columns.find(std::string(q_name.c_str()));
4371     if (it != aggr.columns.end()) {
4372       priv |= it->second;
4373     }
4374 
4375   } else {
4376     if (grant->version != grant_version) {
4377       grant->grant_table = table_hash_search(
4378           sctx->host().str, sctx->ip().str, db_name, sctx->priv_user().str,
4379           table_name, false);         /* purecov: inspected */
4380       grant->version = grant_version; /* purecov: inspected */
4381     }
4382 
4383     if (!(grant_table = grant->grant_table))
4384       priv = grant->privilege;
4385     else {
4386       grant_column =
4387           column_hash_search(grant_table, field_name, strlen(field_name));
4388       if (!grant_column)
4389         priv = (grant->privilege | grant_table->privs);
4390       else
4391         priv = (grant->privilege | grant_table->privs | grant_column->rights);
4392     }
4393   }
4394   return priv;
4395 }
4396 
4397 /*
4398   Make a clear-text version of the requested privilege.
4399 */
4400 
get_privilege_desc(char * to,uint max_length,ulong access)4401 void get_privilege_desc(char *to, uint max_length, ulong access) {
4402   uint pos;
4403   char *start = to;
4404   DBUG_ASSERT(max_length >= 30);  // For end ', ' removal
4405 
4406   if (access) {
4407     max_length--;  // Reserve place for end-zero
4408     for (pos = 0; access; pos++, access >>= 1) {
4409       if ((access & 1) &&
4410           global_acls_vector[pos].length() + (uint)(to - start) < max_length) {
4411         to = my_stpcpy(to, global_acls_vector[pos].c_str());
4412         *to++ = ',';
4413         *to++ = ' ';
4414       }
4415     }
4416     to--;  // Remove end ' '
4417     to--;  // Remove end ','
4418   }
4419   *to = 0;
4420 }
4421 
4422 /**
4423   Iterate a string by comma separation and apply a function on each chunk
4424   separated by the commas.
4425   @param str The string to be iterated
4426   @param f   A function which will receive the comma separated strings.
4427 
4428 */
iterate_comma_separated_quoted_string(std::string str,const std::function<bool (const std::string)> & f)4429 void iterate_comma_separated_quoted_string(
4430     std::string str, const std::function<bool(const std::string)> &f) {
4431   if (str.length() == 0) return;
4432   std::string::iterator i = str.begin();
4433   std::stringstream ss;
4434   bool q1 = false;
4435   bool q2 = false;
4436   bool q3 = false;
4437   while (i != str.end()) {
4438     if (!q2 && !q3 && *i == '`') {
4439       if (q1)
4440         q1 = false;
4441       else
4442         q1 = true;
4443     } else if (!q1 && !q3 && *i == '\'') {
4444       if (q2)
4445         q2 = false;
4446       else
4447         q2 = true;
4448     } else if (!q1 && !q2 && *i == '\'') {
4449       if (q3)
4450         q3 = false;
4451       else
4452         q3 = true;
4453     } else if (q1 == false && q2 == false && q3 == false && *i == ',') {
4454       if (f(ss.str())) return;
4455       ss.str("");
4456       ++i;
4457       continue;
4458     } else if (q1 == false && q2 == false && q3 == false && *i == ' ') {
4459       ++i;
4460       continue;
4461     }
4462     ss << *i;
4463     ++i;
4464   }
4465   f(ss.str());
4466 }
4467 
4468 /**
4469   Return the unquoted authorization id as a user,host-tuple
4470   @param str The quoted or unquoted string representation of an authid
4471 
4472   @return The unquoted authorization id as a user,host-tuple
4473 */
4474 
get_authid_from_quoted_string(std::string str)4475 std::pair<std::string, std::string> get_authid_from_quoted_string(
4476     std::string str) {
4477   std::string::iterator i;
4478   std::stringstream user;
4479   std::stringstream host;
4480   int ct = 0;
4481   bool q1 = false;
4482   bool q2 = false;
4483   bool q3 = false;
4484   for (i = str.begin(); i != str.end(); ++i) {
4485     if (!q2 && !q3 && *i == '`') {
4486       if (q1)
4487         q1 = false;
4488       else
4489         q1 = true;
4490       continue;
4491     } else if (!q1 && !q3 && *i == '\'') {
4492       if (q2)
4493         q2 = false;
4494       else
4495         q2 = true;
4496       continue;
4497     } else if (!q1 && !q2 && *i == '"') {
4498       if (q3)
4499         q3 = false;
4500       else
4501         q3 = true;
4502       continue;
4503     } else if (q1 == false && q2 == false && q3 == false && *i == '@') {
4504       ++ct;
4505       continue;
4506     } else if (q1 == false && q2 == false && q3 == false && *i == ' ') {
4507       continue;
4508     }
4509     if (ct == 0) {
4510       user << *i;
4511     } else {
4512       host << *i;
4513     }
4514   }
4515   if (ct == 0 && !user.str().empty()) host << '%';
4516   return std::make_pair(user.str(), host.str());
4517 }
4518 
operator ==(const std::pair<Role_id,bool> & rid,const Auth_id_ref & ref)4519 bool operator==(const std::pair<Role_id, bool> &rid, const Auth_id_ref &ref) {
4520   return (rid.first.user() == std::string(ref.first.str, ref.first.length) &&
4521           rid.first.host() == std::string(ref.second.str, ref.second.length));
4522 }
4523 
operator ==(const Auth_id_ref & ref,const std::pair<Role_id,bool> & rid)4524 bool operator==(const Auth_id_ref &ref, const std::pair<Role_id, bool> &rid) {
4525   return operator==(rid, ref);
4526 }
4527 
get_privilege_access_maps(ACL_USER * acl_user,const List_of_auth_id_refs * using_roles,ulong * access,Db_access_map * db_map,Db_access_map * db_wild_map,Table_access_map * table_map,SP_access_map * sp_map,SP_access_map * func_map,List_of_granted_roles * granted_roles,Grant_acl_set * with_admin_acl,Dynamic_privileges * dynamic_acl,Restrictions & restrictions)4528 void get_privilege_access_maps(
4529     ACL_USER *acl_user, const List_of_auth_id_refs *using_roles, ulong *access,
4530     Db_access_map *db_map, Db_access_map *db_wild_map,
4531     Table_access_map *table_map, SP_access_map *sp_map, SP_access_map *func_map,
4532     List_of_granted_roles *granted_roles, Grant_acl_set *with_admin_acl,
4533     Dynamic_privileges *dynamic_acl, Restrictions &restrictions) {
4534   DBUG_TRACE;
4535   DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
4536   List_of_auth_id_refs activated_roles_ref;
4537   boost::graph_traits<Granted_roles_graph>::edge_iterator ei, ei_end;
4538   /* First we check the current users access control */
4539   // Get global access
4540   *access = acl_user->access;
4541 
4542   DBUG_PRINT("info", ("Global access for acl_user %s@%s is %lu", acl_user->user,
4543                       acl_user->host.get_host(), acl_user->access));
4544   // Get database access
4545   get_database_access_map(acl_user, db_map, db_wild_map);
4546   // Get table- and column privileges
4547   get_table_access_map(acl_user, table_map);
4548   // get stored procedure privileges
4549   get_sp_access_map(acl_user, sp_map, proc_priv_hash.get());
4550   // get user function privileges
4551   get_sp_access_map(acl_user, func_map, func_priv_hash.get());
4552   // get dynamic privileges
4553   get_dynamic_privileges(acl_user, dynamic_acl);
4554   /* Find out the existing restrictions of the current user. */
4555   restrictions = acl_restrictions->find_restrictions(acl_user);
4556 
4557   /* We don't support role hierarchies for anonymous accounts. */
4558   if (acl_user->user == nullptr) return;
4559 
4560   /*
4561     Temporarily apply the mandatory roles on this user for the sake of
4562     generating an Acl_map.
4563   */
4564   std::vector<Role_id> mandatory_roles;
4565   std::vector<Role_vertex_descriptor> mandatory_roles_vertex_ids;
4566   get_mandatory_roles(&mandatory_roles);
4567 
4568   /* Only check roles if there are any granted roles at all */
4569   boost::tie(ei, ei_end) = boost::edges(*g_granted_roles);
4570   Role_vertex_descriptor user_vertex, active_role_vertex;
4571 
4572   std::string user_key = create_authid_str_from(acl_user);
4573   Role_index_map::iterator user_vertex_it = g_authid_to_vertex->find(user_key);
4574   bool has_granted_roles = (ei != ei_end);
4575   std::set<Role_id> granted_active_roles;
4576   std::set<Role_id> all_granted_roles;
4577   std::set<Role_id> all_active_roles;
4578   boost::vector_property_map<boost::default_color_type> v_color(
4579       boost::num_vertices(*g_granted_roles));
4580 
4581   Get_access_maps vis(acl_user, access, db_map, db_wild_map, table_map, sp_map,
4582                       func_map, with_admin_acl, dynamic_acl, &restrictions);
4583   if (has_granted_roles || mandatory_roles.size() > 0) {
4584     bool acl_user_has_vertex = (user_vertex_it != g_authid_to_vertex->end());
4585     if (!acl_user_has_vertex) return;
4586     user_vertex = user_vertex_it->second;
4587     if (acl_user_has_vertex) {
4588       get_granted_roles(user_vertex, [&](const Role_id &rid, bool with_admin) {
4589         all_granted_roles.insert(rid);
4590         granted_roles->push_back(std::make_pair(rid, with_admin));
4591       });
4592       for (auto &rid : mandatory_roles) {
4593         all_granted_roles.insert(rid);
4594       }
4595       for (auto &rid : *using_roles) {
4596         Role_id id(rid.first, rid.second);
4597         all_active_roles.insert(id);
4598       }
4599 
4600       std::set_intersection(
4601           all_granted_roles.begin(), all_granted_roles.end(),
4602           all_active_roles.begin(), all_active_roles.end(),
4603           std::inserter(granted_active_roles, granted_active_roles.begin()));
4604       int vertex_count = 0;
4605       for (auto &&rid : granted_active_roles) {
4606         String rolestr;
4607         append_identifier(&rolestr, rid.user().c_str(), rid.user().length());
4608         rolestr.append('@');
4609         append_identifier(&rolestr, rid.host().c_str(), rid.host().length());
4610         Role_index_map::iterator rindex =
4611             g_authid_to_vertex->find(rolestr.c_ptr_quick());
4612         if (rindex == g_authid_to_vertex->end()) {
4613           THD *thd = current_thd;
4614           if (thd) {
4615             push_warning_printf(thd, Sql_condition::SL_WARNING,
4616                                 ER_UNKNOWN_AUTHID,
4617                                 "Illegal role %s@%s was ignored.",
4618                                 rid.user().c_str(), rid.host().c_str());
4619           }
4620           continue;  // next role
4621         }
4622         active_role_vertex = rindex->second;
4623         if (vertex_count == 0) {
4624           /* breadth_first_search will initialize our v_color vector for us */
4625           boost::breadth_first_search(*g_granted_roles, active_role_vertex,
4626                                       boost::color_map(v_color).visitor(vis));
4627         } else {
4628           /* breadth_first_visit will not reinitialize the v_color vector */
4629           boost::breadth_first_visit(*g_granted_roles, active_role_vertex,
4630                                      boost::color_map(v_color).visitor(vis));
4631           ++vertex_count;
4632         }
4633 
4634         /*
4635           An active edge might have been granted WITH ADMIN; make sure
4636           we update the temporary edge with this property.
4637         */
4638         Role_edge_descriptor edge;
4639         bool found;
4640         boost::tie(edge, found) =
4641             boost::edge(user_vertex, active_role_vertex, *g_granted_roles);
4642         if (found) {
4643           int with_admin_opt =
4644               boost::get(boost::edge_capacity_t(), *g_granted_roles)[edge];
4645           if (with_admin_opt) {
4646             with_admin_acl->insert(std::string(rolestr.c_ptr()));
4647           }
4648         }
4649 
4650       }  // end for
4651     }    // if user_vertex_it != g_authid_to_vertex->end()
4652   }      // if has_granted_roles
4653   DBUG_PRINT("info", ("Global access for role user %s@%s is %lu",
4654                       acl_user->user, acl_user->host.get_host(), *access));
4655 }
4656 
4657 /**
4658   SHOW GRANTS FOR user USING [ALL | role [,role ...]]
4659   @param thd         thread handler
4660   @param lex_user    The user,host descriptor
4661   @param using_roles An forward iterable container of LEX_STRING std::pair
4662   @param show_mandatory_roles true means mandatory roles are listed
4663   @param have_using_clause true means there's a non-empty USING clause specified
4664 
4665   @return Success status
4666 */
mysql_show_grants(THD * thd,LEX_USER * lex_user,const List_of_auth_id_refs & using_roles,bool show_mandatory_roles,bool have_using_clause)4667 bool mysql_show_grants(THD *thd, LEX_USER *lex_user,
4668                        const List_of_auth_id_refs &using_roles,
4669                        bool show_mandatory_roles, bool have_using_clause) {
4670   int error = 0;
4671   ACL_USER *acl_user = nullptr;
4672   char buff[1024];
4673   DBUG_TRACE;
4674 
4675   DBUG_ASSERT(initialized);
4676   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
4677   if (!acl_cache_lock.lock()) return true;
4678 
4679   acl_user = find_acl_user(lex_user->host.str, lex_user->user.str, true);
4680   if (!acl_user) {
4681     my_error(ER_NONEXISTING_GRANT, MYF(0), lex_user->user.str,
4682              lex_user->host.str);
4683     return true;
4684   }
4685 
4686   /*
4687     For a SHOW GRANTS USING one needs to check if the session has access to
4688     the roles specied in the USING clause.
4689     But if there's no USING clause the list of active session roles is used
4690     insetad. But since this list is a copy into the thread's security context
4691     the active roles might have stopped being granted into the global
4692     structure.
4693     Thus a check if these are still granted might fail.
4694     So we skip the check if there's no explict USING knowing that the check
4695     has already been perfromed for these when they were set.
4696   */
4697   if (have_using_clause) {
4698     std::vector<Role_id> mandatory_roles;
4699     get_mandatory_roles(&mandatory_roles);
4700     List_of_granted_roles granted_roles;
4701     get_granted_roles(lex_user, &granted_roles);
4702     for (auto &role_ref : using_roles) {
4703       std::string authid(create_authid_str_from(role_ref));
4704       if (find(granted_roles.begin(), granted_roles.end(), authid) ==
4705           granted_roles.end()) {
4706         if (std::find_if(mandatory_roles.begin(), mandatory_roles.end(),
4707                          [&](const Role_id &id) -> bool {
4708                            std::string id_str, rid_str;
4709                            id.auth_str(&id_str);
4710                            Role_id rid(role_ref.first, role_ref.second);
4711                            rid.auth_str(&rid_str);
4712                            return (Role_id(role_ref.first, role_ref.second) ==
4713                                    id);
4714                          }) == mandatory_roles.end()) {
4715           my_error(ER_ROLE_NOT_GRANTED, MYF(0), role_ref.first.str,
4716                    role_ref.second.str, lex_user->user.str, lex_user->host.str);
4717           return true;
4718         }
4719       }
4720     }
4721   }
4722 
4723   Item_string *field = new Item_string("", 0, &my_charset_latin1);
4724   List<Item> field_list;
4725   field->max_length = 1024;
4726   strxmov(buff, "Grants for ", lex_user->user.str, "@", lex_user->host.str,
4727           NullS);
4728   field->item_name.set(buff);
4729   field_list.push_back(field);
4730   if (thd->send_result_metadata(&field_list,
4731                                 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) {
4732     return true;
4733   }
4734   // aggregate over the active role and user privileges
4735   Db_access_map db_map;
4736   Db_access_map db_wild_map;
4737   Table_access_map table_map;
4738   SP_access_map sp_map;
4739   SP_access_map func_map;
4740   Grant_acl_set with_admin_acl;
4741   Dynamic_privileges dynamic_acl;
4742   List_of_granted_roles granted_roles;
4743   Restrictions restrictions(thd->mem_root);
4744   ulong access;
4745   table_map.set_thd(thd);
4746   get_privilege_access_maps(acl_user, &using_roles, &access, &db_map,
4747                             &db_wild_map, &table_map, &sp_map, &func_map,
4748                             &granted_roles, &with_admin_acl, &dynamic_acl,
4749                             restrictions);
4750   String output;
4751   make_global_privilege_statement(thd, access, acl_user, &output);
4752   Protocol *protocol = thd->get_protocol();
4753   protocol->start_row();
4754   protocol->store_string(output.ptr(), output.length(), output.charset());
4755   protocol->end_row();
4756 
4757   make_dynamic_privilege_statement(thd, acl_user, protocol, dynamic_acl);
4758   make_database_privilege_statement(thd, acl_user, protocol, db_map,
4759                                     db_wild_map, restrictions.db());
4760   make_table_privilege_statement(thd, acl_user, protocol, table_map);
4761   make_sp_privilege_statement(thd, acl_user, protocol, sp_map, 0);
4762   make_sp_privilege_statement(thd, acl_user, protocol, func_map, 1);
4763   make_proxy_privilege_statement(thd, acl_user, protocol);
4764   make_roles_privilege_statement(thd, acl_user, protocol, granted_roles,
4765                                  show_mandatory_roles);
4766   make_with_admin_privilege_statement(thd, acl_user, protocol, with_admin_acl,
4767                                       granted_roles);
4768 
4769   my_eof(thd);
4770   return error;
4771 }
4772 
roles_graphml(THD * thd,String * str)4773 void roles_graphml(THD *thd, String *str) {
4774   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
4775   if (!acl_cache_lock.lock()) return;
4776   boost::dynamic_properties dp;
4777   dp.property("name", boost::get(boost::vertex_name_t(), *g_granted_roles));
4778   dp.property("color", boost::get(boost::edge_capacity_t(), *g_granted_roles));
4779   std::stringstream ss;
4780   boost::write_graphml(ss, *g_granted_roles, dp, true);
4781   std::string out = ss.str();
4782   str->copy(out.c_str(), out.length(), system_charset_info);
4783 }
4784 
4785 /**
4786   Remove db access privileges.
4787 
4788   @param thd    Current thread execution context.
4789   @param table  Pointer to a TABLE object for opened table mysql.db.
4790   @param lex_user  User information.
4791 
4792   @return  Operation result
4793     @retval  0    OK.
4794     @retval  1    Application error happen, it is allowed
4795                   continuing of operations.
4796     @retval  < 0  Engine error.
4797 */
4798 
remove_db_access_privileges(THD * thd,TABLE * table,const LEX_USER & lex_user)4799 static int remove_db_access_privileges(THD *thd, TABLE *table,
4800                                        const LEX_USER &lex_user) {
4801   ACL_DB *acl_db;
4802   int revoked, result = 0;
4803 
4804   /*
4805     Because acl_dbs shrink and may re-order as privileges are removed,
4806     removal occurs in a repeated loop until no more privileges are revoked.
4807   */
4808   do {
4809     for (revoked = 0, acl_db = acl_dbs->begin(); acl_db != acl_dbs->end();) {
4810       const char *user, *host;
4811 
4812       if (!(user = acl_db->user)) user = "";
4813       if (!(host = acl_db->host.get_host())) host = "";
4814 
4815       if (!strcmp(lex_user.user.str, user) &&
4816           !strcmp(lex_user.host.str, host)) {
4817         int ret =
4818             replace_db_table(thd, table, acl_db->db, lex_user, ~(ulong)0, true);
4819         if (!ret) {
4820           /*
4821             Don't increment loop variable as replace_db_table deleted the
4822             current element in acl_dbs.
4823           */
4824           revoked = 1;
4825           continue;
4826         } else if (ret < 0)
4827           return ret;  // Something went wrong
4828         else
4829           /*
4830             For the case when replace_db_table() returns 1 we continue
4831             iteration in order to remove all db access privileges. It is safe
4832             since this function is called as part of handling the statement
4833             REVOKE ALL.
4834           */
4835           result = 1;
4836       }
4837       ++acl_db;
4838     }
4839   } while (revoked);
4840 
4841   return result;
4842 }
4843 
4844 /**
4845   Remove column access privileges.
4846 
4847   @param thd                 Thread handler.
4848   @param tables_priv_table   Pointer to a TABLE object for opened table
4849                              mysql.tables_priv_table.
4850   @param columns_priv_table  Pointer to a TABLE object for opened table
4851                              mysql.columns_priv_table.
4852   @param lex_user            User information.
4853 
4854   @return  Operation result
4855     @retval  0    OK.
4856     @retval  1    Application error happen, it is allowed
4857                   continuing of operations.
4858     @retval  < 0  Engine error.
4859 */
4860 
remove_column_access_privileges(THD * thd,TABLE * tables_priv_table,TABLE * columns_priv_table,const LEX_USER & lex_user)4861 static int remove_column_access_privileges(THD *thd, TABLE *tables_priv_table,
4862                                            TABLE *columns_priv_table,
4863                                            const LEX_USER &lex_user) {
4864   bool revoked = false;
4865   int result = 0;
4866   /*
4867     Remove column access.
4868     Because column_priv_hash shrink and may re-order as privileges are removed,
4869     removal occurs in a repeated loop until no more privileges are revoked.
4870   */
4871   do {
4872     revoked = false;
4873     for (auto it = column_priv_hash->begin(), next_it = it;
4874          it != column_priv_hash->end(); it = next_it) {
4875       /*
4876         Store an iterator pointing to the next element now, since
4877         replace_table_table could delete elements, invalidating "it".
4878       */
4879       next_it = next(it);
4880 
4881       const char *user, *host;
4882       GRANT_TABLE *grant_table = it->second.get();
4883       if (!(user = grant_table->user)) user = "";
4884       if (!(host = grant_table->host.get_host())) host = "";
4885 
4886       if (!strcmp(lex_user.user.str, user) &&
4887           !strcmp(lex_user.host.str, host)) {
4888         // Hold on to grant_table if it gets deleted, since we use it below.
4889         std::unique_ptr<GRANT_TABLE, Destroy_only<GRANT_TABLE>>
4890             deleted_grant_table;
4891 
4892         int ret = replace_table_table(
4893             thd, grant_table, &deleted_grant_table, tables_priv_table, lex_user,
4894             grant_table->db, grant_table->tname, ~(ulong)0, 0, true);
4895         if (ret < 0) {
4896           return ret;
4897         } else if (ret > 0) {
4898           /*
4899             For the case when replace_table_table() returns 1 we continue
4900             iteration in order to remove all column access privileges.
4901           */
4902           result = 1;
4903           revoked = true;
4904           break;
4905         } else {
4906           if (!grant_table->cols) {
4907             revoked = true;
4908             break;
4909           }
4910           List<LEX_COLUMN> columns;
4911           ret = replace_column_table(thd, grant_table, columns_priv_table,
4912                                      lex_user, columns, grant_table->db,
4913                                      grant_table->tname, ~(ulong)0, true);
4914           if (!ret) {
4915             revoked = true;
4916             break;
4917           }
4918           /*
4919             If we come there then the variable ret always has a value < 0 since
4920             the actual argument 'columns' doesn't contain any elements
4921           */
4922           DBUG_ASSERT(ret < 0);
4923 
4924           return ret;
4925         }
4926       }
4927     }
4928   } while (revoked);
4929 
4930   return result;
4931 }
4932 
4933 /**
4934   Remove procedure access privileges.
4935 
4936   @param thd                 Thread handler.
4937   @param procs_priv_table    Pointer to a TABLE object for opened table
4938                              mysql.procs_priv_table.
4939   @param lex_user            User information.
4940 
4941   @return  Operation result.
4942     @retval  0    OK.
4943     @retval  1    Application error happen, it is allowed
4944                   continuing of operations.
4945     @retval  < 0  Engine error.
4946 */
4947 
remove_procedure_access_privileges(THD * thd,TABLE * procs_priv_table,const LEX_USER & lex_user)4948 static int remove_procedure_access_privileges(THD *thd, TABLE *procs_priv_table,
4949                                               const LEX_USER &lex_user) {
4950   /* Remove procedure access */
4951   int result = 0;
4952   bool revoked;
4953   for (int is_proc = 0; is_proc < 2; is_proc++) do {
4954       malloc_unordered_multimap<std::string,
4955                                 unique_ptr_destroy_only<GRANT_NAME>> *hash =
4956           is_proc ? proc_priv_hash.get() : func_priv_hash.get();
4957       revoked = false;
4958       for (auto it = hash->begin(), next_it = it; it != hash->end();
4959            it = next_it) {
4960         /*
4961           Store an iterator pointing to the next element now, since
4962           replace_routine_table could delete elements, invalidating "it".
4963         */
4964         next_it = next(it);
4965 
4966         const char *user, *host;
4967         GRANT_NAME *grant_proc = it->second.get();
4968         if (!(user = grant_proc->user)) user = "";
4969         if (!(host = grant_proc->host.get_host())) host = "";
4970 
4971         if (!strcmp(lex_user.user.str, user) &&
4972             !strcmp(lex_user.host.str, host)) {
4973           int ret = replace_routine_table(
4974               thd, grant_proc, procs_priv_table, lex_user, grant_proc->db,
4975               grant_proc->tname, is_proc, ~(ulong)0, true);
4976 
4977           if (!ret) {
4978             revoked = true;
4979             continue;
4980           } else if (ret < 0)
4981             return ret;
4982           else
4983             /*
4984               For the case when replace_routine_table() returns 1 we continue
4985               iteration in order to remove all procedure access privileges.
4986               It is safe since this function is called as part of handling
4987               the statement REVOKE ALL.
4988             */
4989             result = 1;
4990         }
4991       }
4992     } while (revoked);
4993 
4994   return result;
4995 }
4996 
4997 /*
4998   Revoke all privileges from a list of users.
4999 
5000   SYNOPSIS
5001     mysql_revoke_all()
5002     thd                         The current thread.
5003     list                        The users to revoke all privileges from.
5004 
5005   RETURN
5006     > 0         Error. Error message already sent.
5007     0           OK.
5008     < 0         Error. Error message not yet sent.
5009 */
5010 
mysql_revoke_all(THD * thd,List<LEX_USER> & list)5011 bool mysql_revoke_all(THD *thd, List<LEX_USER> &list) {
5012   bool result = false;
5013   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
5014   bool transactional_tables;
5015   int ret = 0;
5016   DBUG_TRACE;
5017 
5018   /*
5019     This statement will be replicated as a statement, even when using
5020     row-based replication.  The binlog state will be cleared here to
5021     statement based replication and will be reset to the originals
5022     values when we are out of this function scope
5023   */
5024   Save_and_Restore_binlog_format_state binlog_format_state(thd);
5025   if ((ret = open_grant_tables(thd, tables, &transactional_tables)))
5026     return ret != 1;
5027 
5028   { /* Critical section */
5029     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
5030 
5031     if (!acl_cache_lock.lock()) {
5032       commit_and_close_mysql_tables(thd);
5033       return true;
5034     }
5035 
5036     if (check_system_user_privilege(thd, list)) {
5037       commit_and_close_mysql_tables(thd);
5038       return true;
5039     }
5040 
5041     TABLE *dynpriv_table = tables[ACL_TABLES::TABLE_DYNAMIC_PRIV].table;
5042     LEX_USER *lex_user, *tmp_lex_user;
5043     List_iterator<LEX_USER> user_list(list);
5044 
5045     while ((tmp_lex_user = user_list++)) {
5046       ulong what_to_set = 0;
5047       if (!(lex_user = get_current_user(thd, tmp_lex_user))) {
5048         result = true;
5049         continue;
5050       }
5051       ACL_USER *acl_user =
5052           find_acl_user(lex_user->host.str, lex_user->user.str, true);
5053       if (acl_user == nullptr) {
5054         result = true;
5055         continue;
5056       }
5057 
5058       Update_dynamic_privilege_table update_table(thd, dynpriv_table);
5059       if ((result = revoke_all_dynamic_privileges(
5060                lex_user->user, lex_user->host, update_table))) {
5061         break;
5062       }
5063       /* copy password expire attributes to individual user */
5064       lex_user->alter_status = thd->lex->alter_password;
5065 
5066       acl_table::Pod_user_what_to_update what_to_update;
5067       what_to_update.m_what = (what_to_set | ACCESS_RIGHTS_ATTR);
5068       ulong rights = ~(ulong)0;
5069       DB_restrictions db_restrictions(nullptr);
5070       Restrictions restrictions(nullptr);
5071       std::unique_ptr<Restrictions_aggregator> aggregator =
5072           Restrictions_aggregator_factory::create(thd, acl_user, nullptr,
5073                                                   rights, false);
5074       if (aggregator) {
5075         if (aggregator->generate(db_restrictions)) {
5076           result = true;
5077           continue;
5078         }
5079         what_to_update.m_what |= USER_ATTRIBUTES;
5080         what_to_update.m_user_attributes |=
5081             acl_table::USER_ATTRIBUTE_RESTRICTIONS;
5082         restrictions.set_db(db_restrictions);
5083       }
5084       if ((ret = replace_user_table(thd, tables[ACL_TABLES::TABLE_USER].table,
5085                                     lex_user, rights, true, false,
5086                                     what_to_update, &restrictions))) {
5087         result = true;
5088         if (ret < 0) break;
5089 
5090         continue;
5091       }
5092 
5093       int ret1, ret2, ret3;
5094       if ((ret1 = remove_db_access_privileges(
5095                thd, tables[ACL_TABLES::TABLE_DB].table, *lex_user)) < 0 ||
5096           (ret2 = remove_column_access_privileges(
5097                thd, tables[ACL_TABLES::TABLE_TABLES_PRIV].table,
5098                tables[ACL_TABLES::TABLE_COLUMNS_PRIV].table, *lex_user)) < 0 ||
5099           (ret3 = remove_procedure_access_privileges(
5100                thd, tables[ACL_TABLES::TABLE_PROCS_PRIV].table, *lex_user)) <
5101               0) {
5102         result = true;  // Something went wrong
5103         break;
5104       } else if (ret1 || ret2 || ret3) {
5105         result = true;
5106         continue;
5107       }
5108     }  // end while
5109 
5110     DBUG_EXECUTE_IF("force_mysql_revoke_all_fail", { result = 1; });
5111 
5112     if (result && !thd->is_error()) my_error(ER_REVOKE_GRANTS, MYF(0));
5113 
5114     result = log_and_commit_acl_ddl(thd, transactional_tables);
5115     get_global_acl_cache()->increase_version();
5116   } /* Critical section */
5117 
5118   /* Notify storage engines */
5119   if (!result) {
5120     acl_notify_htons(thd, SQLCOM_REVOKE_ALL, &list);
5121   }
5122 
5123   return result;
5124 }
5125 
5126 /**
5127   If the defining user for a routine does not exist, then the ACL lookup
5128   code should raise two errors which we should intercept.  We convert the more
5129   descriptive error into a warning, and consume the other.
5130 
5131   If any other errors are raised, then we set a flag that should indicate
5132   that there was some failure we should complain at a higher level.
5133 */
5134 class Silence_routine_definer_errors : public Internal_error_handler {
5135  public:
Silence_routine_definer_errors()5136   Silence_routine_definer_errors() : is_grave(false) {}
5137 
handle_condition(THD *,uint sql_errno,const char *,Sql_condition::enum_severity_level * level,const char *)5138   virtual bool handle_condition(THD *, uint sql_errno, const char *,
5139                                 Sql_condition::enum_severity_level *level,
5140                                 const char *) {
5141     if (*level == Sql_condition::SL_ERROR) {
5142       if (sql_errno == ER_NONEXISTING_PROC_GRANT) {
5143         /* Convert the error into a warning. */
5144         *level = Sql_condition::SL_WARNING;
5145         return true;
5146       } else
5147         is_grave = true;
5148     }
5149 
5150     return false;
5151   }
5152 
has_errors() const5153   bool has_errors() const { return is_grave; }
5154 
5155  private:
5156   bool is_grave;
5157 };
5158 
5159 /**
5160   Revoke privileges for all users on a stored procedure.  Use an error handler
5161   that converts errors about missing grants into warnings.
5162 
5163   @param thd       The current thread.
5164   @param sp_db     DB of the stored procedure
5165   @param sp_name   Name of the stored procedure
5166   @param is_proc   True if this is a SP rather than a function.
5167 
5168   @retval
5169     false       OK.
5170   @retval
5171     true        Error. Error message not yet sent.
5172 */
5173 
sp_revoke_privileges(THD * thd,const char * sp_db,const char * sp_name,bool is_proc)5174 bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
5175                           bool is_proc) {
5176   bool revoked;
5177   int int_result;
5178   bool result = false;
5179   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
5180   Silence_routine_definer_errors error_handler;
5181   bool transactional_tables;
5182   DBUG_TRACE;
5183 
5184   if (0 != (int_result = open_grant_tables(thd, tables, &transactional_tables)))
5185     return int_result != 1;
5186 
5187   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
5188   if (!acl_cache_lock.lock()) {
5189     commit_and_close_mysql_tables(thd);
5190     return true;
5191   }
5192 
5193   /* Be sure to pop this before exiting this scope! */
5194   thd->push_internal_handler(&error_handler);
5195 
5196   /*
5197     This statement will be replicated as a statement, even when using
5198     row-based replication.  The binlog state will be cleared here to
5199     statement based replication and will be reset to the originals
5200     values when we are out of this function scope
5201   */
5202   Save_and_Restore_binlog_format_state binlog_format_state(thd);
5203 
5204   /* Remove procedure access */
5205   malloc_unordered_multimap<std::string, unique_ptr_destroy_only<GRANT_NAME>>
5206       *hash = is_proc ? proc_priv_hash.get() : func_priv_hash.get();
5207   do {
5208     revoked = false;
5209     for (auto it = hash->begin(), next_it = it; it != hash->end();
5210          it = next_it) {
5211       /*
5212         Store an iterator pointing to the next element now, since
5213         replace_routine_table could delete elements, invalidating "it".
5214       */
5215       next_it = next(it);
5216       GRANT_NAME *grant_proc = it->second.get();
5217       if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
5218           !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name)) {
5219         LEX_USER lex_user;
5220         lex_user.user.str = grant_proc->user;
5221         lex_user.user.length = strlen(grant_proc->user);
5222         lex_user.host.str =
5223             grant_proc->host.get_host() ? grant_proc->host.get_host() : "";
5224         lex_user.host.length = grant_proc->host.get_host()
5225                                    ? strlen(grant_proc->host.get_host())
5226                                    : 0;
5227 
5228         int ret = replace_routine_table(
5229             thd, grant_proc, tables[4].table, lex_user, grant_proc->db,
5230             grant_proc->tname, is_proc, ~(ulong)0, true);
5231         if (ret < 0) {
5232           result = true;
5233           revoked = false;
5234           break;
5235         } else if (ret == 0) {
5236           revoked = true;
5237           continue;
5238         }
5239       }
5240     }
5241   } while (revoked);
5242 
5243   /* We don't want to write to binlog or notify htons about this. */
5244   result |= log_and_commit_acl_ddl(thd, transactional_tables, nullptr, nullptr,
5245                                    result, false);
5246 
5247   thd->pop_internal_handler();
5248   return error_handler.has_errors() || result;
5249 }
5250 
5251 /**
5252   Grant EXECUTE,ALTER privilege for a stored procedure
5253 
5254   @param      thd                  The current thread.
5255   @param      sp_db                DB of the stored procedure.
5256   @param      sp_name              Name of the stored procedure
5257   @param      is_proc              True if this is a SP rather than a function
5258 
5259   @retval false Success
5260   @retval true An error occurred. Error message not yet sent.
5261 */
5262 
sp_grant_privileges(THD * thd,const char * sp_db,const char * sp_name,bool is_proc)5263 bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
5264                          bool is_proc) {
5265   TABLE_LIST tables[1];
5266   List<LEX_USER> user_list;
5267   bool result = true;
5268   Dummy_error_handler error_handler;
5269 
5270   DBUG_TRACE;
5271 
5272   LEX_CSTRING sctx_user = thd->security_context()->priv_user();
5273   LEX_CSTRING sctx_host = thd->security_context()->priv_host();
5274   LEX_USER *combo =
5275       LEX_USER::alloc(thd, (LEX_STRING *)&sctx_user, (LEX_STRING *)&sctx_host);
5276   if (combo == nullptr) return true;
5277 
5278   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
5279   if (!acl_cache_lock.lock()) return true;
5280 
5281   ACL_USER *au = find_acl_user(combo->host.str, combo->user.str, false);
5282   if (au == nullptr) {
5283     result = true;
5284     goto end;
5285   }
5286 
5287   acl_cache_lock.unlock();
5288 
5289   new (&tables[0]) TABLE_LIST();
5290   user_list.empty();
5291 
5292   tables->db = sp_db;
5293   tables->table_name = tables->alias = sp_name;
5294 
5295   lex_string_strmake(thd->mem_root, &combo->user, combo->user.str,
5296                      strlen(combo->user.str));
5297   lex_string_strmake(thd->mem_root, &combo->host, combo->host.str,
5298                      strlen(combo->host.str));
5299 
5300   if (user_list.push_back(combo)) return true;
5301 
5302   thd->lex->ssl_type = SSL_TYPE_NOT_SPECIFIED;
5303   thd->lex->ssl_cipher = thd->lex->x509_subject = thd->lex->x509_issuer =
5304       nullptr;
5305   memset(&thd->lex->mqh, 0, sizeof(thd->lex->mqh));
5306   /* set default values */
5307   thd->lex->alter_password.cleanup();
5308 
5309   combo->alter_status = thd->lex->alter_password;
5310 
5311   /*
5312     Only care about whether the operation failed or succeeded
5313     as all errors will be handled later.
5314   */
5315   thd->push_internal_handler(&error_handler);
5316   result = mysql_routine_grant(thd, tables, is_proc, user_list,
5317                                DEFAULT_CREATE_PROC_ACLS, false, false);
5318   thd->pop_internal_handler();
5319 end:
5320   return result;
5321 }
5322 
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)5323 static bool update_schema_privilege(THD *thd, TABLE *table, char *buff,
5324                                     const char *db, const char *t_name,
5325                                     const char *column, size_t col_length,
5326                                     const char *priv, size_t priv_length,
5327                                     const char *is_grantable) {
5328   int i = 2;
5329   CHARSET_INFO *cs = system_charset_info;
5330   DBUG_ASSERT(assert_acl_cache_read_lock(thd));
5331   restore_record(table, s->default_values);
5332   table->field[0]->store(buff, strlen(buff), cs);
5333   table->field[1]->store(STRING_WITH_LEN("def"), cs);
5334   if (db) table->field[i++]->store(db, strlen(db), cs);
5335   if (t_name) table->field[i++]->store(t_name, strlen(t_name), cs);
5336   if (column) table->field[i++]->store(column, col_length, cs);
5337   table->field[i++]->store(priv, priv_length, cs);
5338   table->field[i]->store(is_grantable, strlen(is_grantable), cs);
5339   return schema_table_store_record(thd, table);
5340 }
5341 
5342 /*
5343   fill effective privileges for table
5344 
5345   SYNOPSIS
5346     fill_effective_table_privileges()
5347     thd     thread handler
5348     grant   grants table descriptor
5349     db      db name
5350     table   table name
5351 */
5352 
fill_effective_table_privileges(THD * thd,GRANT_INFO * grant,const char * db,const char * table)5353 void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
5354                                      const char *db, const char *table) {
5355   Security_context *sctx = thd->security_context();
5356   LEX_CSTRING priv_user = sctx->priv_user();
5357   DBUG_TRACE;
5358   DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
5359                        sctx->priv_host().str,
5360                        (sctx->ip().length ? sctx->ip().str : "(NULL)"),
5361                        (priv_user.str ? priv_user.str : "(NULL)"), db, table));
5362   /*
5363     This function is not intended for derived tables which doesn't have a
5364     name. If this happens something is wrong.
5365   */
5366   /* --skip-grants */
5367   if (!initialized) {
5368     DBUG_PRINT("info", ("skip grants"));
5369     grant->privilege = ~NO_ACCESS;  // everything is allowed
5370     DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
5371     return;
5372   }
5373   DBUG_PRINT("info", ("Effective table privileges are deduced from active roles"
5374                       " (%lu)",
5375                       (unsigned long)sctx->get_active_roles()->size()));
5376   std::string db_name = db ? db : "";
5377   if (sctx->get_active_roles()->size() > 0) {
5378     /* global privileges */
5379     grant->privilege = sctx->master_access(db_name);
5380     LEX_CSTRING str_db = {db, strlen(db)};
5381     /* db privileges */
5382     grant->privilege |= sctx->db_acl(str_db);
5383     LEX_CSTRING str_table = {table, strlen(table)};
5384     /* table privileges */
5385     grant->privilege |= sctx->table_acl(str_db, str_table);
5386     grant->grant_table = nullptr;
5387     DBUG_PRINT("info", ("Role used: %s db: %s db-acl: %lu all-acl: %lu ",
5388                         sctx->get_active_roles()->at(0).first.str, db,
5389                         sctx->db_acl(str_db), grant->privilege));
5390   } else {
5391     /* global privileges */
5392     grant->privilege = sctx->master_access(db_name);
5393 
5394     /* db privileges */
5395     grant->privilege |= acl_get(thd, sctx->host().str, sctx->ip().str,
5396                                 priv_user.str, db, false);
5397 
5398     DEBUG_SYNC(thd, "fill_effective_table_privileges");
5399     /* table privileges */
5400     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
5401     if (!acl_cache_lock.lock(false)) return;
5402 
5403     if (grant->version != grant_version) {
5404       grant->grant_table =
5405           table_hash_search(sctx->host().str, sctx->ip().str, db, priv_user.str,
5406                             table, false); /* purecov: inspected */
5407       grant->version = grant_version;      /* purecov: inspected */
5408     }
5409     if (grant->grant_table != nullptr) {
5410       grant->privilege |= grant->grant_table->privs;
5411     }
5412   }
5413 
5414   // Allow SELECT privilege for INFORMATION_SCHEMA.
5415   if (is_infoschema_db(db)) grant->privilege |= SELECT_ACL;
5416 
5417   DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
5418 }
5419 
acl_check_proxy_grant_access(THD * thd,const char * host,const char * user,bool with_grant MY_ATTRIBUTE ((unused)))5420 bool acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
5421                                   bool with_grant MY_ATTRIBUTE((unused))) {
5422   DBUG_TRACE;
5423   DBUG_PRINT("info",
5424              ("user=%s host=%s with_grant=%d", user, host, (int)with_grant));
5425   DBUG_ASSERT(initialized);
5426   /* replication slave thread can do anything */
5427   if (thd->slave_thread) {
5428     DBUG_PRINT("info", ("replication slave"));
5429     return false;
5430   }
5431 
5432   /*
5433     one can grant proxy for self to others.
5434     Security context in THD contains two pairs of (user,host):
5435     1. (user,host) pair referring to inbound connection.
5436     2. (priv_user,priv_host) pair obtained from mysql.user table after doing
5437         authnetication of incoming connection.
5438     Privileges should be checked wrt (priv_user, priv_host) tuple, because
5439     (user,host) pair obtained from inbound connection may have different
5440     values than what is actually stored in mysql.user table and while granting
5441     or revoking proxy privilege, user is expected to provide entries mentioned
5442     in mysql.user table.
5443   */
5444   if (!strcmp(thd->security_context()->priv_user().str, user) &&
5445       !my_strcasecmp(system_charset_info, host,
5446                      thd->security_context()->priv_host().str)) {
5447     DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
5448                         thd->security_context()->priv_user().str, user, host,
5449                         thd->security_context()->priv_host().str));
5450     return false;
5451   }
5452   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
5453   if (!acl_cache_lock.lock()) return true;
5454 
5455   /* check for matching WITH PROXY rights */
5456   for (ACL_PROXY_USER *proxy = acl_proxy_users->begin();
5457        proxy != acl_proxy_users->end(); ++proxy) {
5458     DEBUG_SYNC(thd, "before_proxy_matches");
5459     if (proxy->matches(thd->security_context()->host().str,
5460                        thd->security_context()->user().str,
5461                        thd->security_context()->ip().str, user, false) &&
5462         proxy->get_with_grant()) {
5463       DBUG_PRINT("info", ("found"));
5464       return false;
5465     }
5466   }
5467 
5468   my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
5469            thd->security_context()->user().str,
5470            thd->security_context()->host_or_ip().str);
5471   return true;
5472 }
5473 
5474 /**
5475   Grantee is of form 'user'@'hostname', so add +1 for '@' and +4 for the
5476   single qoutes. And +1 for null byte too.
5477 
5478   Note that we use USERNAME_LENGTH and not USERNAME_CHAR_LENGTH here
5479   because the username can be utf8.
5480 */
5481 static const int GRANTEE_MAX_BUFF_LENGTH =
5482     USERNAME_LENGTH + 1 + HOSTNAME_LENGTH + 4 + 1;
5483 
fill_schema_user_privileges(THD * thd,TABLE_LIST * tables,Item *)5484 int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, Item *) {
5485   int error = 0;
5486   ACL_USER *acl_user;
5487   ulong want_access;
5488   char buff[GRANTEE_MAX_BUFF_LENGTH];
5489   TABLE *table = tables->table;
5490   bool no_global_access = check_access(thd, SELECT_ACL, consts::mysql.c_str(),
5491                                        nullptr, nullptr, true, true);
5492   const char *curr_host = thd->security_context()->priv_host_name();
5493   DBUG_TRACE;
5494 
5495   if (!initialized) return 0;
5496 
5497   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
5498   if (!acl_cache_lock.lock()) return 1;
5499 
5500   for (acl_user = acl_users->begin(); acl_user != acl_users->end();
5501        ++acl_user) {
5502     const char *user, *host, *is_grantable = "YES";
5503     if (!(user = acl_user->user)) user = "";
5504     if (!(host = acl_user->host.get_host())) host = "";
5505 
5506     if (no_global_access &&
5507         (strcmp(thd->security_context()->priv_user().str, user) ||
5508          my_strcasecmp(system_charset_info, curr_host, host)))
5509       continue;
5510 
5511     want_access = acl_user->access;
5512     if (!(want_access & GRANT_ACL)) is_grantable = "NO";
5513 
5514     strxmov(buff, "'", user, "'@'", host, "'", NullS);
5515     if (!(want_access & ~GRANT_ACL)) {
5516       if (update_schema_privilege(thd, table, buff, nullptr, nullptr, nullptr,
5517                                   0, STRING_WITH_LEN("USAGE"), is_grantable)) {
5518         error = 1;
5519         goto err;
5520       }
5521     } else {
5522       uint priv_id;
5523       ulong j, test_access = want_access & ~GRANT_ACL;
5524       for (priv_id = 0, j = SELECT_ACL; j <= GLOBAL_ACLS; priv_id++, j <<= 1) {
5525         if (test_access & j) {
5526           if (update_schema_privilege(
5527                   thd, table, buff, nullptr, nullptr, nullptr, 0,
5528                   global_acls_vector[priv_id].c_str(),
5529                   global_acls_vector[priv_id].length(), is_grantable)) {
5530             error = 1;
5531             goto err;
5532           }
5533         }
5534       }
5535     }
5536     /* Process all global privileges */
5537     Role_id key(create_authid_from(acl_user));
5538     User_to_dynamic_privileges_map::iterator it, it_end;
5539     std::tie(it, it_end) = g_dynamic_privileges_map->equal_range(key);
5540     for (; it != it_end; ++it) {
5541       size_t str_len = it->second.first.length();
5542       if (it->second.second)
5543         is_grantable = "YES";
5544       else
5545         is_grantable = "NO";
5546       if (update_schema_privilege(thd, table, buff, nullptr, nullptr, nullptr,
5547                                   0, it->second.first.c_str(), str_len,
5548                                   is_grantable)) {
5549         error = 1;
5550         goto err;
5551       }
5552     }
5553   }  // end for each user
5554 
5555 err:
5556   return error;
5557 }
5558 
fill_schema_schema_privileges(THD * thd,TABLE_LIST * tables,Item *)5559 int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, Item *) {
5560   int error = 0;
5561   ACL_DB *acl_db;
5562   ulong want_access;
5563   char buff[GRANTEE_MAX_BUFF_LENGTH];
5564   TABLE *table = tables->table;
5565   bool no_global_access = check_access(thd, SELECT_ACL, consts::mysql.c_str(),
5566                                        nullptr, nullptr, true, true);
5567   const char *curr_host = thd->security_context()->priv_host_name();
5568   DBUG_TRACE;
5569 
5570   if (!initialized) return 0;
5571 
5572   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
5573   if (!acl_cache_lock.lock()) return 1;
5574 
5575   for (acl_db = acl_dbs->begin(); acl_db != acl_dbs->end(); ++acl_db) {
5576     const char *user, *host, *is_grantable = "YES";
5577 
5578     if (!(user = acl_db->user)) user = "";
5579     if (!(host = acl_db->host.get_host())) host = "";
5580 
5581     if (no_global_access &&
5582         (strcmp(thd->security_context()->priv_user().str, user) ||
5583          my_strcasecmp(system_charset_info, curr_host, host)))
5584       continue;
5585 
5586     want_access = acl_db->access;
5587     if (want_access) {
5588       if (!(want_access & GRANT_ACL)) {
5589         is_grantable = "NO";
5590       }
5591       strxmov(buff, "'", user, "'@'", host, "'", NullS);
5592       if (!(want_access & ~GRANT_ACL)) {
5593         if (update_schema_privilege(thd, table, buff, acl_db->db, nullptr,
5594                                     nullptr, 0, STRING_WITH_LEN("USAGE"),
5595                                     is_grantable)) {
5596           error = 1;
5597           goto err;
5598         }
5599       } else {
5600         int cnt;
5601         ulong j, test_access = want_access & ~GRANT_ACL;
5602         for (cnt = 0, j = SELECT_ACL; j <= DB_ACLS; cnt++, j <<= 1)
5603           if (test_access & j) {
5604             if (update_schema_privilege(
5605                     thd, table, buff, acl_db->db, nullptr, nullptr, 0,
5606                     global_acls_vector[cnt].c_str(),
5607                     global_acls_vector[cnt].length(), is_grantable)) {
5608               error = 1;
5609               goto err;
5610             }
5611           }
5612       }
5613     }
5614   }
5615 err:
5616 
5617   return error;
5618 }
5619 
fill_schema_table_privileges(THD * thd,TABLE_LIST * tables,Item *)5620 int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, Item *) {
5621   int error = 0;
5622   char buff[GRANTEE_MAX_BUFF_LENGTH];
5623   TABLE *table = tables->table;
5624   bool no_global_access = check_access(thd, SELECT_ACL, consts::mysql.c_str(),
5625                                        nullptr, nullptr, true, true);
5626   const char *curr_host = thd->security_context()->priv_host_name();
5627   DBUG_TRACE;
5628 
5629   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
5630   if (!acl_cache_lock.lock()) return 1;
5631 
5632   if (column_priv_hash == nullptr) return error;
5633 
5634   for (const auto &key_and_value : *column_priv_hash) {
5635     const char *user, *host, *is_grantable = "YES";
5636     GRANT_TABLE *grant_table = key_and_value.second.get();
5637     if (!(user = grant_table->user)) user = "";
5638     if (!(host = grant_table->host.get_host())) host = "";
5639 
5640     if (no_global_access &&
5641         (strcmp(thd->security_context()->priv_user().str, user) ||
5642          my_strcasecmp(system_charset_info, curr_host, host)))
5643       continue;
5644 
5645     ulong table_access = grant_table->privs;
5646     if (table_access) {
5647       ulong test_access = table_access & ~GRANT_ACL;
5648       /*
5649         We should skip 'usage' privilege on table if
5650         we have any privileges on column(s) of this table
5651       */
5652       if (!test_access && grant_table->cols) continue;
5653       if (!(table_access & GRANT_ACL)) is_grantable = "NO";
5654 
5655       strxmov(buff, "'", user, "'@'", host, "'", NullS);
5656       if (!test_access) {
5657         if (update_schema_privilege(thd, table, buff, grant_table->db,
5658                                     grant_table->tname, nullptr, 0,
5659                                     STRING_WITH_LEN("USAGE"), is_grantable)) {
5660           error = 1;
5661           goto err;
5662         }
5663       } else {
5664         ulong j;
5665         int cnt;
5666         for (cnt = 0, j = SELECT_ACL; j <= TABLE_ACLS; cnt++, j <<= 1) {
5667           if (test_access & j) {
5668             if (update_schema_privilege(
5669                     thd, table, buff, grant_table->db, grant_table->tname,
5670                     nullptr, 0, global_acls_vector[cnt].c_str(),
5671                     global_acls_vector[cnt].length(), is_grantable)) {
5672               error = 1;
5673               goto err;
5674             }
5675           }
5676         }
5677       }
5678     }
5679   }
5680 err:
5681 
5682   return error;
5683 }
5684 
fill_schema_column_privileges(THD * thd,TABLE_LIST * tables,Item *)5685 int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, Item *) {
5686   int error = 0;
5687   char buff[GRANTEE_MAX_BUFF_LENGTH];
5688   TABLE *table = tables->table;
5689   bool no_global_access = check_access(thd, SELECT_ACL, consts::mysql.c_str(),
5690                                        nullptr, nullptr, true, true);
5691   const char *curr_host = thd->security_context()->priv_host_name();
5692   DBUG_TRACE;
5693 
5694   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
5695   if (!acl_cache_lock.lock()) return 1;
5696 
5697   if (column_priv_hash == nullptr) return error;
5698 
5699   for (const auto &key_and_value : *column_priv_hash) {
5700     const char *user, *host, *is_grantable = "YES";
5701     GRANT_TABLE *grant_table = key_and_value.second.get();
5702     if (!(user = grant_table->user)) user = "";
5703     if (!(host = grant_table->host.get_host())) host = "";
5704 
5705     if (no_global_access &&
5706         (strcmp(thd->security_context()->priv_user().str, user) ||
5707          my_strcasecmp(system_charset_info, curr_host, host)))
5708       continue;
5709 
5710     ulong table_access = grant_table->cols;
5711     if (table_access != 0) {
5712       if (!(grant_table->privs & GRANT_ACL)) is_grantable = "NO";
5713 
5714       ulong test_access = table_access & ~GRANT_ACL;
5715       strxmov(buff, "'", user, "'@'", host, "'", NullS);
5716       if (!test_access)
5717         continue;
5718       else {
5719         ulong j;
5720         int cnt;
5721         for (cnt = 0, j = SELECT_ACL; j <= TABLE_ACLS; cnt++, j <<= 1) {
5722           if (test_access & j) {
5723             for (const auto &key_and_value_gt : grant_table->hash_columns) {
5724               GRANT_COLUMN *grant_column = key_and_value_gt.second.get();
5725               if ((grant_column->rights & j) && (table_access & j)) {
5726                 if (update_schema_privilege(
5727                         thd, table, buff, grant_table->db, grant_table->tname,
5728                         grant_column->column.data(),
5729                         grant_column->column.size(),
5730                         global_acls_vector[cnt].c_str(),
5731                         global_acls_vector[cnt].length(), is_grantable)) {
5732                   error = 1;
5733                   goto err;
5734                 }
5735               }
5736             }
5737           }
5738         }
5739       }
5740     }
5741   }
5742 err:
5743 
5744   return error;
5745 }
5746 
is_privileged_user_for_credential_change(THD * thd)5747 bool is_privileged_user_for_credential_change(THD *thd) {
5748   if (thd->slave_thread) return true;
5749   return (!check_access(thd, UPDATE_ACL, consts::mysql.c_str(), nullptr,
5750                         nullptr, true, true) ||
5751           thd->security_context()->check_access(CREATE_USER_ACL,
5752                                                 consts::mysql.c_str(), false));
5753 }
5754 
5755 /**
5756   Check if user has enough privileges for execution of SHOW statement,
5757   which was converted to query to one of I_S tables.
5758 
5759   @param thd    Thread context.
5760   @param table  Table list element for I_S table to be queried..
5761 
5762   @retval false - Success.
5763   @retval true  - Failure.
5764 */
5765 
check_show_access(THD * thd,TABLE_LIST * table)5766 bool check_show_access(THD *thd, TABLE_LIST *table) {
5767   // perform privilege checking for show statements on new dd tables
5768   switch (thd->lex->sql_command) {
5769     case SQLCOM_SHOW_DATABASES: {
5770       return (specialflag & SPECIAL_SKIP_SHOW_DB) &&
5771              check_global_access(thd, SHOW_DB_ACL);
5772     }
5773     case SQLCOM_SHOW_EVENTS: {
5774       const char *db = thd->lex->select_lex->db;
5775       DBUG_ASSERT(db != nullptr);
5776       /*
5777         Nobody has EVENT_ACL for I_S and P_S,
5778         even with a GRANT ALL to *.*,
5779         because these schemas have additional ACL restrictions:
5780         see ACL_internal_schema_registry.
5781 
5782         Yet there are no events in I_S and P_S to hide either,
5783         so this check voluntarily does not enforce ACL for
5784         SHOW EVENTS in I_S or P_S,
5785         to return an empty list instead of an access denied error.
5786 
5787         This is more user friendly, in particular for tools.
5788 
5789         EVENT_ACL is not fine grained enough to differentiate:
5790         - creating / updating / deleting events
5791         - viewing existing events
5792       */
5793       if (!is_infoschema_db(db) && !is_perfschema_db(db) &&
5794           check_access(thd, EVENT_ACL, db, nullptr, nullptr, false, false))
5795         return true;
5796     }
5797     // Fall through
5798     case SQLCOM_SHOW_TABLES:
5799     case SQLCOM_SHOW_TABLE_STATUS:
5800     case SQLCOM_SHOW_TRIGGERS: {
5801       const char *dst_db_name = thd->lex->select_lex->db;
5802       DBUG_ASSERT(dst_db_name != nullptr);
5803       if (!dst_db_name) break;
5804 
5805       // Check if the user has global access
5806       if (check_access(thd, SELECT_ACL, dst_db_name, &thd->col_access, nullptr,
5807                        false, false))
5808         return true;
5809 
5810       // Now check, if user has access to any of database/table/column/routine
5811       if (!(thd->col_access & DB_OP_ACLS) && check_grant_db(thd, dst_db_name)) {
5812         my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
5813                  thd->security_context()->priv_user().str,
5814                  thd->security_context()->priv_host().str, dst_db_name);
5815         return true;
5816       }
5817       return false;
5818     }
5819     case SQLCOM_SHOW_FIELDS:
5820     case SQLCOM_SHOW_KEYS: {
5821       TABLE_LIST *dst_table;
5822       dst_table = table->schema_select_lex->table_list.first;
5823 
5824       DBUG_ASSERT(dst_table);
5825       /*
5826         Open temporary tables to be able to detect them during privilege check.
5827       */
5828       if (open_temporary_tables(thd, dst_table)) return true;
5829 
5830       if (check_access(thd, SELECT_ACL, dst_table->db,
5831                        &dst_table->grant.privilege,
5832                        &dst_table->grant.m_internal, false, false))
5833         return true; /* Access denied */
5834 
5835       /*
5836         Check_grant will grant access if there is any column privileges on
5837         all of the tables thanks to the fourth parameter (bool show_table).
5838       */
5839       if (check_grant(thd, SELECT_ACL, dst_table, true, UINT_MAX, false))
5840         return true; /* Access denied */
5841 
5842       close_thread_tables(thd);
5843       dst_table->table = nullptr;
5844 
5845       /* Access granted */
5846       return false;
5847     }
5848     default:
5849       break;
5850   }
5851 
5852   return false;
5853 }
5854 
5855 /**
5856   check for global access and give descriptive error message if it fails.
5857 
5858   @param thd			Thread handler
5859   @param want_access		Use should have any of these global rights
5860 
5861   @warning
5862     One gets access right if one has ANY of the rights in want_access.
5863     This is useful as one in most cases only need one global right,
5864     but in some case we want to check if the user has SUPER or
5865     REPL_CLIENT_ACL rights.
5866 
5867   @retval
5868     0	ok
5869   @retval
5870     1	Access denied.  In this case an error is sent to the client
5871 */
5872 
check_global_access(THD * thd,ulong want_access)5873 bool check_global_access(THD *thd, ulong want_access) {
5874   DBUG_TRACE;
5875   char command[128];
5876   if (thd->security_context()->check_access(
5877           want_access, thd->db().str ? thd->db().str : "", true))
5878     return false;
5879   get_privilege_desc(command, sizeof(command), want_access);
5880   my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
5881   return true;
5882 }
5883 
5884 /**
5885   Checks foreign key's parent table access.
5886 
5887   @param [in] thd               Thread handler
5888   @param [in] create_info       Create information (like MAX_ROWS, ENGINE or
5889                                 temporary table flag)
5890   @param [in] alter_info        Initial list of columns and indexes for the
5891                                 table to be created
5892 
5893   @retval
5894    false  ok.
5895   @retval
5896    true	  error or access denied. Error is sent to client in this case.
5897 */
check_fk_parent_table_access(THD * thd,HA_CREATE_INFO * create_info,Alter_info * alter_info)5898 bool check_fk_parent_table_access(THD *thd, HA_CREATE_INFO *create_info,
5899                                   Alter_info *alter_info) {
5900   DBUG_ASSERT(alter_info != nullptr);
5901 
5902   handlerton *db_type =
5903       create_info->db_type ? create_info->db_type : ha_default_handlerton(thd);
5904 
5905   // Return if engine does not support Foreign key Constraint.
5906   if (!ha_check_storage_engine_flag(db_type, HTON_SUPPORTS_FOREIGN_KEYS))
5907     return false;
5908 
5909   for (const Key_spec *key : alter_info->key_list) {
5910     if (key->type == KEYTYPE_FOREIGN) {
5911       const Foreign_key_spec *fk_key = down_cast<const Foreign_key_spec *>(key);
5912 
5913       TABLE_LIST parent_table(fk_key->ref_db.str, fk_key->ref_db.length,
5914                               fk_key->ref_table.str, fk_key->ref_table.length,
5915                               fk_key->ref_table.str, TL_IGNORE);
5916 
5917       /*
5918        Check if user has REFERENCES_ACL privilege at table level on
5919        "parent_table".
5920        Having privilege on any of the parent_table column is not
5921        enough so checking whether user has REFERENCES_ACL privilege
5922        at table level here.
5923       */
5924       if ((check_access(thd, REFERENCES_ACL, parent_table.db,
5925                         &parent_table.grant.privilege,
5926                         &parent_table.grant.m_internal, false, true) ||
5927            check_grant(thd, REFERENCES_ACL, &parent_table, false, 1, true)) ||
5928           (parent_table.grant.privilege & REFERENCES_ACL) == 0) {
5929         char fqtn_buff[NAME_LEN + 1 + NAME_LEN + 1];
5930         snprintf(fqtn_buff, sizeof(fqtn_buff), "%s.%s", fk_key->ref_db.str,
5931                  fk_key->ref_table.str);
5932         my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), "REFERENCES",
5933                  thd->security_context()->priv_user().str,
5934                  thd->security_context()->host_or_ip().str, fqtn_buff);
5935 
5936         return true;
5937       }
5938     }
5939   }
5940 
5941   return false;
5942 }
5943 
5944 /**
5945   Examines if a user\@host authid is connected to a role\@role_host authid by
5946   comparing all out-edges if the user\@host vertex in the global role graph.
5947 
5948   @retval true the two vertices are connected (role is granted)
5949   @retval false not connected (role is not granted)
5950 */
check_if_granted_role(LEX_CSTRING user,LEX_CSTRING host,LEX_CSTRING role,LEX_CSTRING role_host)5951 bool check_if_granted_role(LEX_CSTRING user, LEX_CSTRING host, LEX_CSTRING role,
5952                            LEX_CSTRING role_host) {
5953   String key;
5954   append_identifier(&key, user.str, user.length);
5955   key.append('@');
5956   append_identifier(&key, host.str, host.length);
5957   Role_index_map::iterator it =
5958       g_authid_to_vertex->find(std::string(key.c_ptr_quick()));
5959   if (it != g_authid_to_vertex->end()) {
5960     /* Check if role is part of current role graph */
5961     if (find_if_granted_role(it->second, role, role_host)) return true;
5962   }
5963 
5964   /*
5965      No grated role match the requested role for the current user;
5966      Check if a mandatory role is granted instead.
5967    */
5968   std::vector<Role_id> mandatory_roles;
5969   get_mandatory_roles(&mandatory_roles);
5970   for (auto &&rid : mandatory_roles) {
5971     if (rid == Role_id(role, role_host)) return true;
5972   }
5973   return false;
5974 }
5975 
5976 /**
5977   Given a vertex in the roles graph, this function finds a directly connected
5978   vertex given a (role, role_host) tuple. The resulting vertex is returned to
5979   the caller through an out-param.
5980 
5981   @param v Vertex descriptor of the authid which might have a granted role
5982   @param role User name part of an authid
5983   @param role_host Host name part of an authid
5984   @param [out] found_vertex The corresponding vertex of the granted role.
5985 
5986   @return Success state
5987    @retval true The role is granted and the corresponding vertex is returned.
5988    @retval false No such role is granted.
5989 */
find_if_granted_role(Role_vertex_descriptor v,LEX_CSTRING role,LEX_CSTRING role_host,Role_vertex_descriptor * found_vertex)5990 bool find_if_granted_role(Role_vertex_descriptor v, LEX_CSTRING role,
5991                           LEX_CSTRING role_host,
5992                           Role_vertex_descriptor *found_vertex) {
5993   DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
5994   boost::graph_traits<Granted_roles_graph>::out_edge_iterator ei, ei_end;
5995   boost::tie(ei, ei_end) = boost::out_edges(v, *g_granted_roles);
5996   /* Iterate all neighboring vertices */
5997   for (; ei != ei_end; ++ei) {
5998     /* find current user in role graph */
5999     ACL_USER acl_user =
6000         get(boost::vertex_acl_user_t(),
6001             *g_granted_roles)[boost::target(*ei, *g_granted_roles)];
6002     if ((role.length == acl_user.get_username_length()) &&
6003         (role_host.length == acl_user.host.get_host_len()) &&
6004         !strncmp(role.str, acl_user.user, role.length) &&
6005         (role_host.length == 0 ||
6006          !strncmp(role_host.str, acl_user.host.get_host(), role_host.length))) {
6007       /* Found a vertex matching the active role */
6008       if (found_vertex != nullptr)
6009         *found_vertex = boost::target(*ei, *g_granted_roles);
6010       return true;
6011     }
6012   }
6013   return false;
6014 }
6015 
get_granted_roles(Role_vertex_descriptor & v,std::function<void (const Role_id &,bool)> f)6016 void get_granted_roles(Role_vertex_descriptor &v,
6017                        std::function<void(const Role_id &, bool)> f) {
6018   DBUG_TRACE;
6019   DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
6020   boost::graph_traits<Granted_roles_graph>::out_edge_iterator ei, ei_end;
6021   boost::tie(ei, ei_end) = boost::out_edges(v, *g_granted_roles);
6022   /* Iterate all neighboring vertices */
6023   for (; ei != ei_end; ++ei) {
6024     /* find current user in role graph */
6025     ACL_USER acl_user =
6026         get(boost::vertex_acl_user_t(),
6027             *g_granted_roles)[boost::target(*ei, *g_granted_roles)];
6028     auto edge_with_admin =
6029         boost::get(boost::edge_capacity_t(), *g_granted_roles);
6030     int with_admin_opt = edge_with_admin[*ei];
6031     LEX_CSTRING tmp_user, tmp_host;
6032     tmp_user.str = acl_user.user;
6033     tmp_user.length = acl_user.get_username_length();
6034     tmp_host.str = acl_user.host.get_host();
6035     tmp_host.length = acl_user.host.get_host_len();
6036     Role_id id(tmp_user, tmp_host);
6037     f(id, with_admin_opt != 0);
6038   }
6039 }
6040 
6041 /**
6042   Populates a list of authorization IDs that are connected to a specified
6043   graph vertex in the global roles graph.
6044 
6045   The constructed list contains references to a shared memory. The authIDs
6046   are not copied!
6047 
6048   The list of granted roles is /appended/ to the out variable.
6049 
6050   @param v A valid vertex descriptor from the global roles graph
6051   @param [out] granted_roles A list of authorization IDs
6052 */
get_granted_roles(Role_vertex_descriptor & v,List_of_granted_roles * granted_roles)6053 void get_granted_roles(Role_vertex_descriptor &v,
6054                        List_of_granted_roles *granted_roles) {
6055   DBUG_TRACE;
6056   DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
6057   get_granted_roles(v, [&](const Role_id &rid, bool with_admin_opt) {
6058     granted_roles->push_back(std::make_pair(rid, with_admin_opt));
6059   });
6060 }
6061 
6062 /**
6063   Activates all roles granted to the auth_id.
6064 
6065   @param  [in]  acl_user ACL_USER for which all granted roles to be activated.
6066   @param  [in]  sctx     Push the activated role to secruity context
6067 */
activate_all_granted_roles(const ACL_USER * acl_user,Security_context * sctx)6068 void activate_all_granted_roles(const ACL_USER *acl_user,
6069                                 Security_context *sctx) {
6070   DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
6071   std::string key = create_authid_str_from(acl_user);
6072   Role_index_map::iterator it = g_authid_to_vertex->find(key);
6073   if (it == g_authid_to_vertex->end()) return;  // No user vertex founds
6074   get_granted_roles(it->second, [&](const Role_id rid, bool) {
6075     LEX_CSTRING str_user = {rid.user().c_str(), rid.user().length()};
6076     LEX_CSTRING str_host = {rid.host().c_str(), rid.host().length()};
6077     sctx->activate_role(str_user, str_host, false);
6078   });
6079 }
6080 
6081 /**
6082   Activates all the mandatory roles for the current user
6083 
6084   @param  [in]  sctx     Push the activated role to secruity context
6085 */
activate_all_mandatory_roles(Security_context * sctx)6086 void activate_all_mandatory_roles(Security_context *sctx) {
6087   DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
6088   std::vector<Role_id> mandatory_roles;
6089   get_mandatory_roles(&mandatory_roles);
6090   for (auto &rid : mandatory_roles) {
6091     LEX_CSTRING str_user = {rid.user().c_str(), rid.user().length()};
6092     LEX_CSTRING str_host = {rid.host().c_str(), rid.host().length()};
6093     sctx->activate_role(str_user, str_host, false);
6094   }
6095 }
6096 
activate_all_granted_and_mandatory_roles(const ACL_USER * acl_user,Security_context * sctx)6097 void activate_all_granted_and_mandatory_roles(const ACL_USER *acl_user,
6098                                               Security_context *sctx) {
6099   activate_all_granted_roles(acl_user, sctx);
6100   activate_all_mandatory_roles(sctx);
6101 }
6102 
6103 /**
6104   This is a convenience function.
6105   @see get_granted_roles(Role_vertex_descriptor &v,
6106                          List_of_granted_roles *granted_roles)
6107   @param user The authid to check for granted roles
6108   @param [out] granted_roles A list of granted authids
6109 */
6110 
get_granted_roles(LEX_USER * user,List_of_granted_roles * granted_roles)6111 void get_granted_roles(LEX_USER *user, List_of_granted_roles *granted_roles) {
6112   Role_index_map::iterator it;
6113   std::string str_user = create_authid_str_from(user);
6114   if ((it = g_authid_to_vertex->find(str_user)) != g_authid_to_vertex->end()) {
6115     get_granted_roles(it->second, granted_roles);
6116   }
6117 }
6118 
6119 /**
6120   Helper function for func_current_role used for Item_func_current_role.
6121   @param thd The thread handler
6122   @param roles [out] A list of Role_id granted to the current user.
6123 */
get_active_roles(const THD * thd,List_of_granted_roles * roles)6124 void get_active_roles(const THD *thd, List_of_granted_roles *roles) {
6125   /*
6126     We need the order of the current roles to stay consistent across platforms
6127     so we copy the list of active roles here and sort the list.
6128     Copying is crucial as the std::sort algorithms operates on pointers and
6129     not on values which cause all references to become invalid.
6130   */
6131   for (auto &ref : *thd->security_context()->get_active_roles()) {
6132     roles->push_back(std::make_pair(Role_id(ref.first, ref.second), false));
6133   }
6134 }
6135 
6136 /**
6137   Helper function for Item_func_current_role.
6138   @param thd Thread handler
6139   @param active_role [out] Comma separated list of auth ids
6140  */
6141 
func_current_role(const THD * thd,String * active_role)6142 void func_current_role(const THD *thd, String *active_role) {
6143   List_of_granted_roles roles;
6144   get_active_roles(thd, &roles);
6145   if (roles.size() == 0) {
6146     active_role->set_ascii("NONE", 4);
6147     return;
6148   }
6149   std::sort(roles.begin(), roles.end());
6150   bool first = true;
6151   for (auto &rid : roles) {
6152     if (!first) {
6153       active_role->append(',');
6154     } else {
6155       first = false;
6156     }
6157     append_identifier(thd, active_role, rid.first.user().c_str(),
6158                       rid.first.user().length());
6159     active_role->append("@");
6160     append_identifier(thd, active_role, rid.first.host().c_str(),
6161                       rid.first.host().length());
6162   }
6163   return;
6164 }
6165 
6166 /**
6167   Shallow copy a list of default role authorization IDs from an Role_id storage
6168 
6169   @param acl_user A valid authID for which we want the default roles.
6170   @param [out] authlist The target list to be populated. The target list is set
6171                         to empty if no default role is found.
6172 */
get_default_roles(const Auth_id_ref & acl_user,List_of_auth_id_refs & authlist)6173 void get_default_roles(const Auth_id_ref &acl_user,
6174                        List_of_auth_id_refs &authlist) {
6175   if (g_default_roles == nullptr) return;
6176 
6177   authlist.clear();  // Remove all items
6178 
6179   Role_id user(acl_user);
6180   Default_roles::iterator role_it, role_end;
6181   boost::tie(role_it, role_end) = g_default_roles->equal_range(user);
6182   for (; role_it != role_end; ++role_it) {
6183     Auth_id_ref ref = create_authid_from(role_it->second);
6184     authlist.push_back(ref);
6185   }
6186 }
6187 
6188 /**
6189   Copy a list of mandatory role authorization IDs.
6190 
6191   @param [out] mandatory_roles Pointer to the target list to be populated.
6192                                The target list is set to empty if no
6193                                mandatory role is found.
6194 */
lock_and_get_mandatory_roles(std::vector<Role_id> * mandatory_roles)6195 bool lock_and_get_mandatory_roles(std::vector<Role_id> *mandatory_roles) {
6196   DBUG_EXECUTE_IF("simulate_acl_cache_lock_failure", { return true; });
6197   Acl_cache_lock_guard acl_cache_lock(current_thd,
6198                                       Acl_cache_lock_mode::READ_MODE);
6199   if (!acl_cache_lock.lock(false)) return true;
6200 
6201   // Retrieve mandatory roles
6202   get_mandatory_roles(mandatory_roles);
6203   return false;
6204 }
6205 
6206 /**
6207   Removes all default role policies assigned to user. If the user is used as a
6208   default role policy, this policy needs to be removed too.
6209   Removed policies are copied to the vector supplied in the arguments.
6210 
6211   @param thd Thread handler
6212   @param table Open table handler
6213   @param user_auth_id A reference to the authorization ID to clear
6214   @param [out] default_roles The vector to which the removed roles are copied.
6215 
6216   @retval true An error occurred.
6217   @retval false Success
6218 */
clear_default_roles(THD * thd,TABLE * table,const Auth_id_ref & user_auth_id,std::vector<Role_id> * default_roles)6219 bool clear_default_roles(THD *thd, TABLE *table,
6220                          const Auth_id_ref &user_auth_id,
6221                          std::vector<Role_id> *default_roles) {
6222   DBUG_TRACE;
6223   DBUG_ASSERT(assert_acl_cache_write_lock(thd));
6224   Default_roles::iterator role_it, role_end, begin_it;
6225   Role_id user_role_id(user_auth_id);
6226   boost::tie(begin_it, role_end) = g_default_roles->equal_range(user_role_id);
6227   role_it = begin_it;
6228   bool error = false;
6229   for (; role_it != role_end && !error; ++role_it) {
6230     if (default_roles != nullptr) {
6231       default_roles->push_back(role_it->second);
6232     }
6233     Auth_id_ref role_auth_id = create_authid_from(role_it->second);
6234     error = modify_default_roles_in_table(thd, table, user_auth_id,
6235                                           role_auth_id, true);
6236   }
6237   g_default_roles->erase(begin_it, role_end);
6238 
6239   return error;
6240 }
6241 
6242 /**
6243   Drop a specific default role policy given the role- and user names.
6244 
6245   @param thd Thread handler
6246   @param table An open table handler to the default_roles table
6247   @param default_role_policy The role name
6248   @param user The user name
6249 
6250   @retval Error state
6251     @retval true An error occurred
6252     @retval false Success
6253 */
6254 
drop_default_role_policy(THD * thd,TABLE * table,const Auth_id_ref & default_role_policy,const Auth_id_ref & user)6255 bool drop_default_role_policy(THD *thd, TABLE *table,
6256                               const Auth_id_ref &default_role_policy,
6257                               const Auth_id_ref &user) {
6258   Role_id id(user);
6259   auto range = g_default_roles->equal_range(id);
6260   for (; range.first != range.second; ++range.first) {
6261     if (range.first->second == default_role_policy) {
6262       g_default_roles->erase(range.first);
6263       return modify_default_roles_in_table(thd, table, user,
6264                                            default_role_policy, true);
6265     }
6266   }
6267   return false;
6268 }
6269 
6270 /**
6271   Set the default roles to NONE, ALL or list of authorization IDs as
6272   roles, depending upon the role_type argument. It writes to table
6273   mysql.default_roles and binlog.
6274 
6275   @param thd        Thread handler
6276   @param role_type  default role type specified by the user.
6277   @param users      Users for whom the default roles are set.
6278   @param roles      list of default roles to be set.
6279 
6280   @retval true An error occurred and DA is set
6281   @retval false Successful
6282 */
mysql_alter_or_clear_default_roles(THD * thd,role_enum role_type,const List<LEX_USER> * users,const List<LEX_USER> * roles)6283 bool mysql_alter_or_clear_default_roles(THD *thd, role_enum role_type,
6284                                         const List<LEX_USER> *users,
6285                                         const List<LEX_USER> *roles) {
6286   DBUG_TRACE;
6287 
6288   List<LEX_USER> *tmp_users = const_cast<List<LEX_USER> *>(users);
6289   List<LEX_USER> *tmp_roles = const_cast<List<LEX_USER> *>(roles);
6290   List_iterator<LEX_USER> users_it(*tmp_users);
6291   List_iterator<LEX_USER> roles_it;
6292   List_of_auth_id_refs authids;
6293   Auth_id_ref authid;
6294   LEX_USER *user = nullptr;
6295   LEX_USER *role = nullptr;
6296   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
6297   int result = 0;
6298   bool transactional_tables = false;
6299 
6300   /*
6301     This statement will be replicated as a statement, even when using
6302     row-based replication. The binlog state will be cleared here to
6303     statement based replication and will be reset to the originals
6304     values when we are out of this function scope
6305   */
6306   Save_and_Restore_binlog_format_state binlog_format_state(thd);
6307 
6308   if ((result = open_grant_tables(thd, tables, &transactional_tables)))
6309     return result != 1;
6310 
6311   TABLE *table = tables[ACL_TABLES::TABLE_DEFAULT_ROLES].table;
6312 
6313   if (!table) {
6314     my_error(ER_OPEN_ROLE_TABLES, MYF(MY_WME));
6315     return true;
6316   }
6317 
6318   bool ret = false;
6319   { /* Critical section */
6320     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
6321 
6322     if (!acl_cache_lock.lock()) {
6323       commit_and_close_mysql_tables(thd);
6324       return true;
6325     }
6326 
6327     if (check_system_user_privilege(thd, *users)) {
6328       commit_and_close_mysql_tables(thd);
6329       return true;
6330     }
6331 
6332     while ((user = users_it++) && !ret) {
6333       // Check for CURRENT_USER token
6334       user = get_current_user(thd, user);
6335       if (strcmp(thd->security_context()->priv_user().str, user->user.str) !=
6336               0 ||
6337           strcmp(thd->security_context()->priv_host().str, user->host.str) !=
6338               0) {
6339         if (check_access(thd, UPDATE_ACL, consts::mysql.c_str(), nullptr,
6340                          nullptr, true, true) &&
6341             check_global_access(thd, CREATE_USER_ACL)) {
6342           my_error(ER_ACCESS_DENIED_ERROR, MYF(0), user->user.str,
6343                    user->host.str,
6344                    (thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO)));
6345           return true;
6346         }
6347         if (roles != nullptr) {
6348           roles_it = *tmp_roles;
6349           while ((role = roles_it++)) {
6350             if (!is_granted_role(user->user, user->host, role->user,
6351                                  role->host)) {
6352               my_error(ER_ROLE_NOT_GRANTED, MYF(0), role->user.str,
6353                        role->host.str, user->user.str, user->host.str);
6354               return true;
6355             }
6356             authid = std::make_pair(role->user, role->host);
6357             authids.push_back(authid);
6358           }
6359         }
6360       } else {
6361         // Verify that the user actually is granted the role before it is
6362         // set as default.
6363         if (roles != nullptr) {
6364           roles_it = *tmp_roles;
6365           while ((role = roles_it++)) {
6366             if (!is_granted_role(thd->security_context()->priv_user(),
6367                                  thd->security_context()->priv_host(),
6368                                  role->user, role->host)) {
6369               my_error(ER_ROLE_NOT_GRANTED, MYF(0), role->user.str,
6370                        role->host.str, thd->security_context()->priv_user().str,
6371                        thd->security_context()->priv_host().str);
6372               return true;
6373             }
6374             authid = std::make_pair(role->user, role->host);
6375             authids.push_back(authid);
6376           }
6377         }
6378       }
6379 
6380       if (role_type == role_enum::ROLE_NONE) {
6381         authid = create_authid_from(user);
6382         ret = clear_default_roles(thd, table, authid, nullptr);
6383       } else if (role_type == role_enum::ROLE_ALL) {
6384         ret = alter_user_set_default_roles_all(thd, table, user);
6385       } else if (role_type == role_enum::ROLE_NAME) {
6386         ret = alter_user_set_default_roles(thd, table, user, authids);
6387       }
6388 
6389       if (ret) {
6390         my_error(ER_FAILED_DEFAULT_ROLES, MYF(0));
6391       }
6392     }
6393 
6394     ret = log_and_commit_acl_ddl(thd, transactional_tables, nullptr, nullptr,
6395                                  ret);
6396     get_global_acl_cache()->increase_version();
6397   } /* Critical section */
6398 
6399   /* Notify storage engines */
6400   if (!ret) {
6401     acl_notify_htons(thd, SQLCOM_ALTER_USER, users);
6402   }
6403 
6404   return ret;
6405 }
6406 
6407 /**
6408   Set all granted role as default roles. Writes to table mysql.default_roles
6409   and binlog.
6410 
6411   @param thd Thread handler
6412   @param def_role_table Default role table
6413   @param user The user whose default roles are set.
6414 
6415   @retval true An error occurred and DA is set
6416   @retval false Successful
6417 */
6418 
alter_user_set_default_roles_all(THD * thd,TABLE * def_role_table,LEX_USER * user)6419 bool alter_user_set_default_roles_all(THD *thd, TABLE *def_role_table,
6420                                       LEX_USER *user) {
6421   DBUG_ASSERT(assert_acl_cache_write_lock(thd));
6422   std::string authid_role = create_authid_str_from(user);
6423   Role_index_map::iterator it = g_authid_to_vertex->find(authid_role);
6424   if (it == g_authid_to_vertex->end()) {
6425     /* No such user */
6426     my_error(ER_UNKNOWN_AUTHID, MYF(MY_WME), user->user.str, user->host.str);
6427     return true;
6428   }
6429   List_of_granted_roles granted_roles;
6430   get_granted_roles(it->second, &granted_roles);
6431   List_of_auth_id_refs new_default_role_ref;
6432   for (auto &&role : granted_roles) {
6433     Auth_id_ref authid = create_authid_from(role.first);
6434     new_default_role_ref.push_back(authid);
6435   }
6436   std::vector<Role_id> mandatory_roles;
6437   get_mandatory_roles(&mandatory_roles);
6438   for (auto &role : mandatory_roles) {
6439     Auth_id_ref authid = create_authid_from(role);
6440     auto res = std::find(new_default_role_ref.begin(),
6441                          new_default_role_ref.end(), authid);
6442     if (res == new_default_role_ref.end()) {
6443       new_default_role_ref.push_back(authid);
6444     }
6445   }
6446   bool errors = alter_user_set_default_roles(thd, def_role_table, user,
6447                                              new_default_role_ref);
6448 
6449   return errors;
6450 }
6451 
6452 /**
6453   Set the default roles for a particular user.
6454 
6455   @param thd           Thread handle
6456   @param table         Table handle to an open table
6457   @param user          AST component for the user for which we set def roles
6458   @param new_auth_ids  Default roles to set
6459 
6460   @retval true  Operation failed
6461   @retval false Operation was successful.
6462 */
6463 
alter_user_set_default_roles(THD * thd,TABLE * table,LEX_USER * user,const List_of_auth_id_refs & new_auth_ids)6464 bool alter_user_set_default_roles(THD *thd, TABLE *table, LEX_USER *user,
6465                                   const List_of_auth_id_refs &new_auth_ids) {
6466   DBUG_ASSERT(assert_acl_cache_write_lock(thd));
6467   bool errors = false;
6468 
6469   ACL_USER *acl_user = find_acl_user(user->host.str, user->user.str, true);
6470   if (acl_user == nullptr) return true;
6471 
6472   if (new_auth_ids.size() != 0) {
6473     Default_roles::iterator role_it, role_end;
6474     Auth_id_ref user_auth_id = create_authid_from(user);
6475     Role_id user_role_id(user_auth_id);
6476     boost::tie(role_it, role_end) = g_default_roles->equal_range(user_role_id);
6477     for (; role_it != role_end && !errors; ++role_it) {
6478       if (std::find(new_auth_ids.begin(), new_auth_ids.end(),
6479                     role_it->second) == new_auth_ids.end()) {
6480         Auth_id_ref role_auth_id = create_authid_from(role_it->second);
6481         errors = modify_default_roles_in_table(thd, table, user_auth_id,
6482                                                role_auth_id, true);
6483       }
6484     }
6485     List_of_auth_id_refs::const_iterator it = new_auth_ids.begin();
6486     boost::tie(role_it, role_end) = g_default_roles->equal_range(user_role_id);
6487     for (; it != new_auth_ids.end() && !errors; ++it) {
6488       if (find(role_it, role_end, *it) == role_end) {
6489         errors =
6490             modify_default_roles_in_table(thd, table, user_auth_id, *it, false);
6491       }
6492     }
6493     boost::tie(role_it, role_end) = g_default_roles->equal_range(user_role_id);
6494     g_default_roles->erase(role_it, role_end);
6495     it = new_auth_ids.begin();
6496     for (; it != new_auth_ids.end() && !errors; ++it) {
6497       Role_id role_role_id(*it);
6498       g_default_roles->insert(std::make_pair(user_role_id, role_role_id));
6499     }
6500   }
6501 
6502   return errors;
6503 }
6504 
6505 /**
6506   Helper used for producing a key to a key-value-map
6507 */
create_authid_str_from(const LEX_USER * user)6508 std::string create_authid_str_from(const LEX_USER *user) {
6509   String tmp;
6510   append_identifier(&tmp, user->user.str, user->user.length);
6511   tmp.append('@');
6512   append_identifier(&tmp, user->host.str, user->host.length);
6513   return std::string(tmp.c_ptr_quick());
6514 }
6515 
create_authid_from(const LEX_USER * user)6516 Auth_id_ref create_authid_from(const LEX_USER *user) {
6517   Auth_id_ref id;
6518   id = std::make_pair(user->user, user->host);
6519   return id;
6520 }
6521 
create_authid_from(const Role_id & user)6522 Auth_id_ref create_authid_from(const Role_id &user) {
6523   Auth_id_ref id;
6524   LEX_CSTRING lex_user;
6525   lex_user.str = user.user().c_str();
6526   lex_user.length = user.user().length();
6527   LEX_CSTRING lex_host;
6528   lex_host.str = user.host().c_str();
6529   lex_host.length = user.host().length();
6530   id = std::make_pair(lex_user, lex_host);
6531   return id;
6532 }
6533 
create_authid_from(const LEX_CSTRING & user,const LEX_CSTRING & host)6534 Auth_id_ref create_authid_from(const LEX_CSTRING &user,
6535                                const LEX_CSTRING &host) {
6536   return std::make_pair(user, host);
6537 }
6538 
6539 /**
6540   Helper used for producing a key to a key-value-map
6541 */
create_authid_str_from(const ACL_USER * user)6542 std::string create_authid_str_from(const ACL_USER *user) {
6543   String tmp;
6544   size_t length = user->get_username_length();
6545   append_identifier(&tmp, user->user, length);
6546   tmp.append("@");
6547   append_identifier(&tmp, user->host.get_host(), user->host.get_host_len());
6548   return std::string(tmp.c_ptr_quick());
6549 }
6550 
create_authid_str_from(const Auth_id_ref & user)6551 std::string create_authid_str_from(const Auth_id_ref &user) {
6552   String tmp;
6553   append_identifier(&tmp, user.first.str, user.first.length);
6554   tmp.append("@");
6555   append_identifier(&tmp, user.second.str, user.second.length);
6556   return std::string(tmp.c_ptr_quick());
6557 }
6558 
create_authid_from(const ACL_USER * user)6559 Auth_id_ref create_authid_from(const ACL_USER *user) {
6560   Auth_id_ref id;
6561   LEX_CSTRING username;
6562   LEX_CSTRING host;
6563   username.str = user->user;
6564   username.length = user->get_username_length();
6565   host.str = user->host.get_host();
6566   host.length = user->host.get_host_len();
6567   id = std::make_pair(username, host);
6568   return id;
6569 }
6570 
6571 /**
6572   Reset active roles
6573 
6574   @param [in] thd THD handle
6575 
6576   @returns status of resetting active roles
6577     @retval false Success
6578     @retval true  Error
6579 */
mysql_set_active_role_none(THD * thd)6580 bool mysql_set_active_role_none(THD *thd) {
6581   DBUG_TRACE;
6582   bool ret = false;
6583   Roles::Role_activation role_activation(thd, thd->security_context(),
6584                                          role_enum::ROLE_NONE, nullptr, false);
6585   ret = role_activation.activate();
6586   if (!ret) my_ok(thd);
6587   return ret;
6588 }
6589 
6590 /**
6591    Activates all the default roles in the current security context
6592 
6593    This function acquires the Acl_cache_lock_guard in read lock.
6594 
6595    @param thd A valid THD handle
6596 
6597    @return Error code
6598      @retval 0 Success; the specified role was activated.
6599      @retval != 0 Failure. DA is set.
6600 */
mysql_set_role_default(THD * thd)6601 bool mysql_set_role_default(THD *thd) {
6602   DBUG_TRACE;
6603   bool ret = false;
6604   Roles::Role_activation role_activation(thd, thd->security_context(),
6605                                          role_enum::ROLE_DEFAULT, nullptr);
6606   ret = role_activation.activate();
6607   if (!ret) my_ok(thd);
6608   return ret;
6609 }
6610 
6611 /**
6612    Activates all granted role in the current security context
6613 
6614    This function acquires the acl_user->lock mutex.
6615 
6616    @param thd A valid THD handle
6617    @param except_users A pointer to a list of LEX_USER objects which represent
6618  roles that shouldn't be activated.
6619 
6620    @return Error code
6621      @retval 0 Success; the specified role was activated.
6622      @retval != 0 Failure. DA is set.
6623 */
mysql_set_active_role_all(THD * thd,const List<LEX_USER> * except_users)6624 bool mysql_set_active_role_all(THD *thd, const List<LEX_USER> *except_users) {
6625   DBUG_TRACE;
6626   bool ret = false;
6627   Roles::Role_activation role_activation(thd, thd->security_context(),
6628                                          role_enum::ROLE_ALL, except_users);
6629   ret = role_activation.activate();
6630   if (!ret) my_ok(thd);
6631   return ret;
6632 }
6633 
mysql_set_active_role(THD * thd,const List<LEX_USER> * role_list)6634 bool mysql_set_active_role(THD *thd, const List<LEX_USER> *role_list) {
6635   DBUG_TRACE;
6636   bool ret = false;
6637   Roles::Role_activation role_activation(thd, thd->security_context(),
6638                                          role_enum::ROLE_NAME, role_list);
6639   ret = role_activation.activate();
6640   if (!ret) my_ok(thd);
6641   return ret;
6642 }
6643 
6644 /**
6645   This function works just like check_if_granted_role, but also guarantees that
6646   the proper lock is taken so that the function can be used in a wider context.
6647   @param user The user name part of a authid which should be tested
6648   @param host The host name part of a authid which should be tested
6649   @param role The role name part of the role authid
6650   @param role_host The host name part of the role authid
6651 
6652   @return success value
6653     @retval true The value user\@host was previously granted role\@role_host
6654     @retval false role\@role_host is not granted to user\@host
6655 */
6656 
is_granted_role(LEX_CSTRING user,LEX_CSTRING host,LEX_CSTRING role,LEX_CSTRING role_host)6657 bool is_granted_role(LEX_CSTRING user, LEX_CSTRING host, LEX_CSTRING role,
6658                      LEX_CSTRING role_host) {
6659   bool ret = false;
6660   Acl_cache_lock_guard acl_cache_lock(current_thd,
6661                                       Acl_cache_lock_mode::READ_MODE);
6662   if (!acl_cache_lock.lock(false)) return false;
6663 
6664   ret = check_if_granted_role(user, host, role, role_host);
6665   return ret;
6666 }
6667 
6668 /**
6669   Determine if a role\@role_host authid is a mandatory role.
6670 
6671   @param role                Role name.
6672   @param role_host           Host name of role.
6673   @param[out] is_mandatory   Pointer to boolean hold status of check.
6674 
6675   @retval true if failed to determine. e.g., ACL lock acquire failed.
6676   @retval false otherwise.
6677 */
is_mandatory_role(LEX_CSTRING role,LEX_CSTRING role_host,bool * is_mandatory)6678 bool is_mandatory_role(LEX_CSTRING role, LEX_CSTRING role_host,
6679                        bool *is_mandatory) {
6680   // Fetch all mandatory role.
6681   std::vector<Role_id> mandatory_roles;
6682   if (lock_and_get_mandatory_roles(&mandatory_roles)) return true;
6683 
6684   // Check if role is in mandatory role list.
6685   *is_mandatory = false;
6686   for (auto &&rid : mandatory_roles) {
6687     if (rid == Role_id(role, role_host)) {
6688       *is_mandatory = true;
6689       break;
6690     }
6691   }
6692   return false;
6693 }
6694 
6695 /**
6696   Grant one privilege to one user
6697   @param str_priv           Dynamic privilege being granted
6698   @param str_user           Username part of the grantee
6699   @param str_host           Hostname part of the grantee
6700   @param with_grant_option  Flag that determines if grantee can manage the
6701   dynamic privilege
6702   @param update_table       Table update handler
6703 
6704   @return Error state
6705     @retval true An error occurred. DA must be checked.
6706     @retval false Success
6707 
6708 */
grant_dynamic_privilege(const LEX_CSTRING & str_priv,const LEX_CSTRING & str_user,const LEX_CSTRING & str_host,bool with_grant_option,Update_dynamic_privilege_table & update_table)6709 bool grant_dynamic_privilege(const LEX_CSTRING &str_priv,
6710                              const LEX_CSTRING &str_user,
6711                              const LEX_CSTRING &str_host,
6712                              bool with_grant_option,
6713                              Update_dynamic_privilege_table &update_table) {
6714   try {
6715     const std::string priv(str_priv.str, str_priv.length);
6716     if (!is_dynamic_privilege_registered(priv)) return true;
6717     const Role_id id(str_user, str_host);
6718     /*
6719       Is this grant already present? If so we will make an update by removing
6720       the previous grant only if the grant_option property has changed.
6721     */
6722     auto range = g_dynamic_privileges_map->equal_range(id);
6723     for (auto it = range.first; it != range.second; ++it) {
6724       if (it->second.first == priv) {
6725         /*
6726           WITH GRANT OPTION is cumulative which means that if a previous GRANT
6727           exists we only update it if we're adding GRANT OPTION to it.
6728           We never remove the GRANT OPTION as a result of GRANT statement.
6729         */
6730         if (with_grant_option == true &&
6731             it->second.second != with_grant_option) {
6732           if (update_table(priv, {str_user, str_host}, false,
6733                            Update_dynamic_privilege_table::REVOKE))
6734             return true;
6735           g_dynamic_privileges_map->erase(it);
6736           break;
6737         }
6738         /* If the entry already exist we're done */
6739         return false;
6740       }
6741     }
6742 
6743     if (update_table(priv, {str_user, str_host}, with_grant_option,
6744                      Update_dynamic_privilege_table::GRANT))
6745       return true;
6746     g_dynamic_privileges_map->insert(
6747         std::make_pair(id, std::make_pair(priv, with_grant_option)));
6748   } catch (...) {
6749     return true;
6750   }
6751   return false;
6752 }
6753 
6754 /**
6755   Grant grant option to one user for all dynamic privileges
6756   @param str_user      Username part of the grantee
6757   @param str_host      Hostname part of the grantee
6758   @param update_table  Table update handler
6759 
6760   @return Error state
6761     @retval true An error occurred. DA must be checked.
6762     @retval false Success
6763 
6764 */
grant_grant_option_for_all_dynamic_privileges(const LEX_CSTRING & str_user,const LEX_CSTRING & str_host,Update_dynamic_privilege_table & update_table)6765 bool grant_grant_option_for_all_dynamic_privileges(
6766     const LEX_CSTRING &str_user, const LEX_CSTRING &str_host,
6767     Update_dynamic_privilege_table &update_table) {
6768   try {
6769     Role_id id(str_user, str_host);
6770     /*
6771       For all dynamic privileges associated for a particular user
6772       grant with grant option.
6773     */
6774     auto range = g_dynamic_privileges_map->equal_range(id);
6775     std::vector<std::string> priv_list;
6776     for (auto it = range.first; it != range.second; ++it) {
6777       std::string priv(it->second.first);
6778       if (it->second.second != true) {
6779         /*
6780           if with grant option is not set reovke this privilege and
6781           later update the privilege along with "WITH GRANT OPTION".
6782         */
6783         update_table(priv, {str_user, str_host}, false,
6784                      Update_dynamic_privilege_table::REVOKE);
6785       } else
6786         continue;
6787 
6788       if (update_table(priv, {str_user, str_host}, true,
6789                        Update_dynamic_privilege_table::GRANT))
6790         return true;
6791       /* keep track of privileges, later used to update cache */
6792       priv_list.push_back(priv);
6793     }
6794     for (auto it = priv_list.begin(); it != priv_list.end(); ++it) {
6795       g_dynamic_privileges_map->insert(
6796           std::make_pair(id, std::make_pair(*it, true)));
6797     }
6798   } catch (...) {
6799     return true;
6800   }
6801   return false;
6802 }
6803 
6804 /**
6805   Revoke grant option to one user for all dynamic privileges
6806   @param str_user      Username part of the grantee
6807   @param str_host      Hostname part of the grantee
6808   @param update_table  Table update handler
6809 
6810   @return Error state
6811     @retval true An error occurred. DA must be checked.
6812     @retval false Success
6813 
6814 */
revoke_grant_option_for_all_dynamic_privileges(const LEX_CSTRING & str_user,const LEX_CSTRING & str_host,Update_dynamic_privilege_table & update_table)6815 bool revoke_grant_option_for_all_dynamic_privileges(
6816     const LEX_CSTRING &str_user, const LEX_CSTRING &str_host,
6817     Update_dynamic_privilege_table &update_table) {
6818   try {
6819     Role_id id(str_user, str_host);
6820     /*
6821       For all dynamic privileges associated for a particular user
6822       revoke with grant option.
6823     */
6824     auto range = g_dynamic_privileges_map->equal_range(id);
6825     std::vector<std::string> priv_list;
6826     for (auto it = range.first; it != range.second; ++it) {
6827       std::string priv(it->second.first);
6828       if (it->second.second == true) {
6829         if (update_table(priv, {str_user, str_host}, true,
6830                          Update_dynamic_privilege_table::REVOKE))
6831           return true;
6832       } else
6833         continue;
6834 
6835       if (update_table(priv, {str_user, str_host}, false,
6836                        Update_dynamic_privilege_table::GRANT))
6837         return true;
6838       /* keep track of privileges, later used to update cache */
6839       priv_list.push_back(priv);
6840     }
6841     for (auto it = priv_list.begin(); it != priv_list.end(); ++it) {
6842       g_dynamic_privileges_map->insert(
6843           std::make_pair(id, std::make_pair(*it, false)));
6844     }
6845   } catch (...) {
6846     return true;
6847   }
6848   return false;
6849 }
6850 
6851 /**
6852   Grant needed dynamic privielges to in memory internal auth id.
6853 
6854   @param id            auth id to which privileges needs to be granted
6855   @param priv_list     List of privileges to be added to internal auth id
6856 
6857   @retval True    In case privilege is not registered
6858   @retval False   Success
6859 */
grant_dynamic_privileges_to_auth_id(const Role_id & id,const std::vector<std::string> & priv_list)6860 bool grant_dynamic_privileges_to_auth_id(
6861     const Role_id &id, const std::vector<std::string> &priv_list) {
6862   DBUG_TRACE;
6863   Update_dynamic_privilege_table update_table;
6864 
6865   /* --skip-grants */
6866   if (!initialized) return false;
6867   for (auto it : priv_list) {
6868     LEX_CSTRING priv = {it.c_str(), it.length()};
6869     LEX_CSTRING user = {id.user().c_str(), id.user().length()};
6870     LEX_CSTRING host = {id.host().c_str(), id.host().length()};
6871     if (grant_dynamic_privilege(priv, user, host, false, update_table))
6872       return true;
6873   }
6874   return false;
6875 }
6876 
6877 /**
6878   Revoke dynamic privielges from in memory internal auth id.
6879 
6880   @param id            auth id from which privileges needs to be revoked
6881   @param priv_list     List of privileges to be removed for internal auth id
6882 */
revoke_dynamic_privileges_from_auth_id(const Role_id & id,const std::vector<std::string> & priv_list)6883 void revoke_dynamic_privileges_from_auth_id(
6884     const Role_id &id, const std::vector<std::string> &priv_list) {
6885   DBUG_TRACE;
6886   if (!initialized) return;
6887   Update_dynamic_privilege_table update_table;
6888   for (auto priv_it : priv_list) {
6889     LEX_CSTRING user = {id.user().c_str(), id.user().length()};
6890     LEX_CSTRING host = {id.host().c_str(), id.host().length()};
6891     LEX_CSTRING priv = {priv_it.c_str(), priv_it.length()};
6892     revoke_dynamic_privilege(priv, user, host, update_table);
6893   }
6894 }
6895 
6896 /**
6897   Revoke one privilege from one user.
6898 
6899   @param str_priv      Privilege being revoked
6900   @param str_user      Username part of the grantee
6901   @param str_host      Hostname part of the grantee
6902   @param update_table  Table update handler
6903 
6904   @return Error state
6905     @retval true An error occurred. DA must be checked.
6906     @retval false Success
6907 */
6908 
revoke_dynamic_privilege(const LEX_CSTRING & str_priv,const LEX_CSTRING & str_user,const LEX_CSTRING & str_host,Update_dynamic_privilege_table & update_table)6909 bool revoke_dynamic_privilege(const LEX_CSTRING &str_priv,
6910                               const LEX_CSTRING &str_user,
6911                               const LEX_CSTRING &str_host,
6912                               Update_dynamic_privilege_table &update_table) {
6913   try {
6914     const std::string priv(str_priv.str, str_priv.length);
6915     const Role_id id(str_user, str_host);
6916     if (is_dynamic_privilege_registered(priv)) {
6917       auto range = g_dynamic_privileges_map->equal_range(id);
6918       for (auto it = range.first; it != range.second; ++it) {
6919         if (it->second.first == priv) {
6920           if (update_table(priv, {str_user, str_host}, false,
6921                            Update_dynamic_privilege_table::REVOKE))
6922             return true;
6923           g_dynamic_privileges_map->erase(it);
6924           break;
6925         }
6926       }
6927     } else {
6928       push_warning_printf(
6929           current_thd, Sql_condition::SL_WARNING,
6930           ER_WARN_DA_PRIVILEGE_NOT_REGISTERED,
6931           ER_THD(current_thd, ER_WARN_DA_PRIVILEGE_NOT_REGISTERED),
6932           str_priv.str);
6933     }
6934   } catch (...) {
6935     return true;
6936   }
6937   return false;
6938 }
6939 
6940 /**
6941   Revoke all dynamic global privileges.
6942   @param user The target user name
6943   @param host The target host name
6944   @param update_table Functor for updating a table
6945 
6946   @return Error state
6947     @retval true An error occurred. DA might not be set.
6948     @retval false Success
6949 */
revoke_all_dynamic_privileges(const LEX_CSTRING & user,const LEX_CSTRING & host,Update_dynamic_privilege_table & update_table)6950 bool revoke_all_dynamic_privileges(
6951     const LEX_CSTRING &user, const LEX_CSTRING &host,
6952     Update_dynamic_privilege_table &update_table) {
6953   try {
6954     if (g_dynamic_privileges_map->size() > 0) {
6955       Role_id id(user, host);
6956       auto range = g_dynamic_privileges_map->equal_range(id);
6957       for (auto it = range.first; it != range.second; ++it) {
6958         if (update_table(it->second.first, {user, host}, false,
6959                          Update_dynamic_privilege_table::REVOKE)) {
6960           return true;
6961         }
6962       }
6963       g_dynamic_privileges_map->erase(range.first, range.second);
6964     }
6965   } catch (...) {
6966     return true;
6967   }
6968   return false;
6969 }
6970 
rename_dynamic_grant(const LEX_CSTRING & old_user,const LEX_CSTRING & old_host,const LEX_CSTRING & new_user,const LEX_CSTRING & new_host,Update_dynamic_privilege_table & update_table)6971 bool rename_dynamic_grant(const LEX_CSTRING &old_user,
6972                           const LEX_CSTRING &old_host,
6973                           const LEX_CSTRING &new_user,
6974                           const LEX_CSTRING &new_host,
6975                           Update_dynamic_privilege_table &update_table) {
6976   try {
6977     if (g_dynamic_privileges_map->size() > 0) {
6978       /*
6979         Revoke all privileges using the old authorization identifier but don't
6980         update the cache.
6981       */
6982       Role_id id(old_user, old_host);
6983       auto range = g_dynamic_privileges_map->equal_range(id);
6984       std::vector<Grant_privilege> privileges(
6985           std::distance(range.first, range.second));
6986       int grants_count = 0;
6987       for (auto it = range.first; it != range.second; ++it, ++grants_count) {
6988         privileges[grants_count] =
6989             std::make_pair(it->second.first, it->second.second);
6990       }
6991       for (auto it = range.first; it != range.second; ++it) {
6992         if (update_table(it->second.first, {old_user, old_host}, false,
6993                          Update_dynamic_privilege_table::REVOKE)) {
6994           return true;
6995         }
6996       }
6997       /*
6998         Remove the old entries from the cache
6999       */
7000       g_dynamic_privileges_map->erase(range.first, range.second);
7001 
7002       /*
7003         Grant the new authorization id the same privileges as the old and update
7004         the cache with the new entries.
7005       */
7006       while (grants_count > 0) {
7007         --grants_count;
7008         LEX_CSTRING priv = {privileges[grants_count].first.c_str(),
7009                             privileges[grants_count].first.length()};
7010         if (grant_dynamic_privilege(priv, new_user, new_host,
7011                                     privileges[grants_count].second,
7012                                     update_table)) {
7013           return true;
7014         }
7015       }
7016     }  // end for
7017   } catch (...) {
7018     return true;
7019   }
7020   return false;
7021 }
7022 /**
7023   Initialize the default role map that keeps the content from the
7024   default_roles table.
7025 */
default_roles_init()7026 void default_roles_init() { g_default_roles = new Default_roles; }
7027 
7028 /**
7029   Delete the default role instance
7030 */
default_roles_delete()7031 void default_roles_delete() { delete g_default_roles; }
7032 
7033 /**
7034   Initialize the roles graph artifacts
7035 */
roles_graph_init()7036 void roles_graph_init() {
7037   g_authid_to_vertex = new Role_index_map;
7038   g_granted_roles = new Granted_roles_graph;
7039 }
7040 
7041 /**
7042   Delete the ACL role graph artifacts
7043 */
roles_graph_delete()7044 void roles_graph_delete() {
7045   delete g_granted_roles;
7046   delete g_authid_to_vertex;
7047 }
7048 
7049 /**
7050   Initialize the roles caches that consist of the role graphs related
7051   artifacts and default role map. In theory, default role map is
7052   supposed to be a policy which has to be kept in sync with role graphs.
7053 */
roles_init()7054 void roles_init() {
7055   roles_graph_init();
7056   default_roles_init();
7057 }
7058 
7059 /**
7060   Delete the role caches
7061 */
roles_delete()7062 void roles_delete() {
7063   roles_graph_delete();
7064   default_roles_delete();
7065 }
7066 
dynamic_privileges_init()7067 void dynamic_privileges_init() {
7068   g_dynamic_privileges_map = new User_to_dynamic_privileges_map();
7069 }
7070 
dynamic_privileges_delete()7071 void dynamic_privileges_delete() {
7072   if (g_dynamic_privileges_map) delete g_dynamic_privileges_map;
7073   g_dynamic_privileges_map = nullptr;
7074 }
7075 
get_dynamic_privileges_map()7076 User_to_dynamic_privileges_map *get_dynamic_privileges_map() {
7077   return g_dynamic_privileges_map;
7078 }
7079 
set_dynamic_privileges_map(User_to_dynamic_privileges_map * map)7080 void set_dynamic_privileges_map(User_to_dynamic_privileges_map *map) {
7081   g_dynamic_privileges_map = map;
7082 }
7083 
swap_dynamic_privileges_map(User_to_dynamic_privileges_map * map)7084 User_to_dynamic_privileges_map *swap_dynamic_privileges_map(
7085     User_to_dynamic_privileges_map *map) {
7086   User_to_dynamic_privileges_map *old_map = g_dynamic_privileges_map;
7087   g_dynamic_privileges_map = map;
7088   return old_map;
7089 }
7090 
assert_valid_privilege_id(const List<LEX_USER> * priv_list)7091 bool assert_valid_privilege_id(const List<LEX_USER> *priv_list) {
7092   /*
7093     Because we need to combine the parsing rule of roles with the parsing
7094     rule of dynamic privileges LEX_USER::user is used to carry the name of
7095     the dynamic privilege.
7096   */
7097   for (const LEX_USER &priv : *priv_list) {
7098     Dynamic_privilege_register::iterator it =
7099         get_dynamic_privilege_register()->find(
7100             std::string(priv.user.str, priv.user.length));
7101     if (it == get_dynamic_privilege_register()->end()) {
7102       String error;
7103       error.append("No such privilege identifier: ");
7104       error.append(priv.user.str, priv.user.length);
7105       my_error(ER_UNKNOWN_ERROR, MYF(0), error.c_ptr_quick());
7106       return false;
7107     }
7108   }
7109   return true;
7110 }
7111 
check_authorization_id_string(THD * thd,LEX_STRING & mandatory_roles)7112 bool check_authorization_id_string(THD *thd, LEX_STRING &mandatory_roles) {
7113   bool error = false;
7114   std::string authid_str(mandatory_roles.str, mandatory_roles.length);
7115   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
7116   if (!acl_cache_lock.lock()) {
7117     error = true;
7118   } else {
7119     iterate_comma_separated_quoted_string(
7120         authid_str, [&thd, &error, &mandatory_roles](const std::string item) {
7121           auto el = get_authid_from_quoted_string(item);
7122           if (el.second != "" && el.first == "")
7123             error = true;
7124           else if (thd->security_context()
7125                        ->has_global_grant({el.first, el.second},
7126                                           consts::system_user, true)
7127                        .first) {
7128             // Send error to both, client and server error log.
7129             if (mysqld_server_started) {
7130               my_error(ER_DA_AUTH_ID_WITH_SYSTEM_USER_PRIV_IN_MANDATORY_ROLES,
7131                        MYF(0), el.first.c_str(), el.second.c_str(),
7132                        consts::system_user.c_str());
7133               LogErr(ERROR_LEVEL,
7134                      ER_AUTH_ID_WITH_SYSTEM_USER_PRIV_IN_MANDATORY_ROLES,
7135                      el.first.c_str(), el.second.c_str(),
7136                      consts::system_user.c_str());
7137               error = true;
7138             } else {
7139               LogErr(WARNING_LEVEL,
7140                      ER_WARN_AUTH_ID_WITH_SYSTEM_USER_PRIV_IN_MANDATORY_ROLES,
7141                      el.first.c_str(), el.second.c_str(),
7142                      consts::system_user.c_str());
7143               /*
7144                 It is safe to reset the LEX_STRING since it is allocated in
7145                 the memroot of THD.
7146               */
7147               mandatory_roles.str = empty_c_string;
7148               mandatory_roles.length = 0;
7149             }
7150           }
7151           return error;
7152         });
7153   }
7154   return error;
7155 }
7156 
get_mandatory_roles(std::vector<Role_id> * mandatory_roles)7157 void get_mandatory_roles(std::vector<Role_id> *mandatory_roles) {
7158   mysql_mutex_lock(&LOCK_mandatory_roles);
7159   if (opt_mandatory_roles_cache) {
7160     /* Use pre-parsed auth ids from the cache */
7161     std::copy(g_mandatory_roles->begin(), g_mandatory_roles->end(),
7162               std::back_inserter(*mandatory_roles));
7163     mysql_mutex_unlock(&LOCK_mandatory_roles);
7164     return;
7165   }
7166   g_mandatory_roles->clear();
7167   /*
7168     We set this flag to indicate that we've already parsed the mandatory_roles
7169     option SQL variable.
7170   */
7171   opt_mandatory_roles_cache = true;
7172   std::string role_str;
7173   role_str.append(opt_mandatory_roles.str, opt_mandatory_roles.length);
7174   iterate_comma_separated_quoted_string(role_str, [&mandatory_roles](
7175                                                       const std::string item) {
7176     auto el = get_authid_from_quoted_string(item);
7177     if (el.second == "") el.second = "%";
7178     Role_id role_id(el.first, el.second);
7179     if (role_id.user() == "") {
7180       LogErr(WARNING_LEVEL, ER_ANONYMOUS_AUTH_ID_NOT_ALLOWED_IN_MANDATORY_ROLES,
7181              role_id.user().c_str(), role_id.host().c_str());
7182     } else if (find_acl_user(role_id.host().c_str(), role_id.user().c_str(),
7183                              true) != nullptr) {
7184       if (std::find(g_mandatory_roles->begin(), g_mandatory_roles->end(),
7185                     role_id) == g_mandatory_roles->end()) {
7186         mandatory_roles->push_back(role_id);
7187         g_mandatory_roles->push_back(role_id);
7188       }
7189     } else {
7190       LogErr(WARNING_LEVEL, ER_UNKNOWN_AUTH_ID_IN_MANDATORY_ROLE,
7191              role_id.user().c_str(), role_id.host().c_str());
7192     }
7193     return false;  // continue iterating
7194   });
7195   std::sort(g_mandatory_roles->begin(), g_mandatory_roles->end());
7196   mysql_mutex_unlock(&LOCK_mandatory_roles);
7197 }
7198 
update_mandatory_roles(void)7199 void update_mandatory_roles(void) {
7200   mysql_mutex_assert_owner(&LOCK_mandatory_roles);
7201   opt_mandatory_roles_cache = false;
7202   get_global_acl_cache()->increase_version();
7203 }
7204 
Default_local_authid(const THD * thd)7205 Default_local_authid::Default_local_authid(const THD *thd) : m_thd(thd) {}
7206 
7207 /**
7208  Check if the security context can be created as a local authid
7209  @param[out] sctx The authid to be checked.
7210  @return Success status
7211   @retval true an error occurred
7212   @retval false success
7213 */
precheck(Security_context * sctx MY_ATTRIBUTE ((unused)))7214 bool Default_local_authid::precheck(
7215     Security_context *sctx MY_ATTRIBUTE((unused))) {
7216   return false;
7217 }
7218 
7219 /**
7220  Create a local authid without modifying any tables.
7221  @param[out] sctx The authid that will be extended with a user profile
7222  @return Success status
7223   @retval true an error occurred
7224   @retval false success
7225 */
create(Security_context * sctx MY_ATTRIBUTE ((unused)))7226 bool Default_local_authid::create(
7227     Security_context *sctx MY_ATTRIBUTE((unused))) {
7228   return false;
7229 }
7230 
Grant_temporary_dynamic_privileges(const THD * thd,std::vector<std::string> privs)7231 Grant_temporary_dynamic_privileges::Grant_temporary_dynamic_privileges(
7232     const THD *thd, std::vector<std::string> privs)
7233     : m_thd(thd), m_privs(std::move(privs)) {}
7234 
precheck(Security_context * sctx MY_ATTRIBUTE ((unused)))7235 bool Grant_temporary_dynamic_privileges::precheck(
7236     Security_context *sctx MY_ATTRIBUTE((unused))) {
7237   return false;
7238 }
7239 
7240 /**
7241  Grant dynamic privileges to an in-memory global authid
7242  @param sctx The authid to grant privileges to.
7243  @return Success status
7244   @retval true an error occurred
7245   @retval false success
7246  */
grant_privileges(Security_context * sctx)7247 bool Grant_temporary_dynamic_privileges::grant_privileges(
7248     Security_context *sctx) {
7249   return grant_dynamic_privileges_to_auth_id(
7250       Role_id(sctx->priv_user(), sctx->priv_host()), m_privs);
7251 }
7252 
operator ()(Security_context * sctx)7253 void Drop_temporary_dynamic_privileges::operator()(Security_context *sctx) {
7254   revoke_dynamic_privileges_from_auth_id(
7255       Role_id(sctx->priv_user(), sctx->priv_host()), m_privs);
7256 }
7257 
Grant_temporary_static_privileges(const THD * thd,ulong privs)7258 Grant_temporary_static_privileges::Grant_temporary_static_privileges(
7259     const THD *thd, ulong privs)
7260     : m_thd(thd), m_privs(privs) {}
7261 
precheck(Security_context * sctx MY_ATTRIBUTE ((unused)))7262 bool Grant_temporary_static_privileges::precheck(
7263     Security_context *sctx MY_ATTRIBUTE((unused))) {
7264   return false;
7265 }
7266 
grant_privileges(Security_context * sctx)7267 bool Grant_temporary_static_privileges::grant_privileges(
7268     Security_context *sctx) {
7269   sctx->set_master_access(m_privs);
7270   return false;
7271 }
7272 
apply_pre_constructed_policies(Security_context * sctx)7273 bool Security_context_factory::apply_pre_constructed_policies(
7274     Security_context *sctx) {
7275   bool error = true;
7276   while (error) {
7277     if (m_user_profile) {
7278       // 1. Precheck conditions for creating the authid under current policy
7279       if (m_user_profile(sctx, Security_context_policy::Precheck)) break;
7280       // 2. Create the authid under the given policy
7281       if (m_user_profile(sctx, Security_context_policy::Execute)) break;
7282     }
7283     if (m_privileges) {
7284       // 3. Check preconditions for assigning privileges under the current
7285       //    policy
7286       if (m_privileges(sctx, Security_context_policy::Precheck)) break;
7287       // 4. Assign the privileges
7288       if (m_privileges(sctx, Security_context_policy::Execute)) break;
7289     }
7290     if (m_static_privileges) {
7291       // 5. Check preconditions for assigning privileges under the current
7292       //    policy
7293       if (m_static_privileges(sctx, Security_context_policy::Precheck)) break;
7294       // 6. Assign static privileges
7295       if (m_static_privileges(sctx, Security_context_policy::Execute)) break;
7296     }
7297     error = false;
7298   }
7299   if (error == false && m_drop_policy) sctx->set_drop_policy(m_drop_policy);
7300   return error;
7301 }
7302 
create(MEM_ROOT * mem_root)7303 Sctx_ptr<Security_context> Security_context_factory::create(
7304     MEM_ROOT *mem_root) {
7305   /* Setup default Security context */
7306   Security_context *sctx = new Security_context(mem_root);
7307   sctx->assign_user(m_user.c_str(), m_user.length());
7308   sctx->assign_host(m_host.c_str(), m_host.length());
7309   sctx->assign_priv_user(m_user.c_str(), m_user.length());
7310   sctx->assign_priv_host(m_host.c_str(), m_host.length());
7311 
7312   /* check if policies applied  successfully */
7313   if (apply_pre_constructed_policies(sctx)) {
7314     /* Each specific policy must raise its own errors */
7315     return nullptr;
7316   }
7317 
7318   return Sctx_ptr<Security_context>(sctx, [](Security_context *ptr) {
7319     if (ptr->has_drop_policy()) {
7320       ptr->execute_drop_policy();
7321       if (ptr->has_executed_drop_policy()) delete ptr;
7322     }
7323   });
7324 }
7325 
operator ==(const Role_id & a,const std::string & b)7326 bool operator==(const Role_id &a, const std::string &b) {
7327   std::string tmp;
7328   a.auth_str(&tmp);
7329   return tmp == b;
7330 }
7331 
operator ==(const std::pair<Role_id,bool> & a,const std::string & b)7332 bool operator==(const std::pair<Role_id, bool> &a, const std::string &b) {
7333   return a.first == b;
7334 }
7335 
operator ==(const Role_id & a,const Auth_id_ref & b)7336 bool operator==(const Role_id &a, const Auth_id_ref &b) {
7337   return ((a.user().length() == b.first.length) &&
7338           (a.host().length() == b.second.length) &&
7339           strncmp(a.user().c_str(), b.first.str, b.first.length) == 0 &&
7340           my_strcasecmp(system_charset_info, a.host().c_str(), b.second.str) ==
7341               0);
7342 }
7343 
operator ==(const Auth_id_ref & a,const Role_id & b)7344 bool operator==(const Auth_id_ref &a, const Role_id &b) { return b == a; }
7345 
operator ==(const std::pair<const Role_id,const Role_id> & a,const Auth_id_ref & b)7346 bool operator==(const std::pair<const Role_id, const Role_id> &a,
7347                 const Auth_id_ref &b) {
7348   return ((a.second.user().length() == b.first.length) &&
7349           (a.second.host().length() == b.second.length) &&
7350           strncmp(a.second.user().c_str(), b.first.str, b.first.length) == 0 &&
7351           my_strcasecmp(system_charset_info, a.second.host().c_str(),
7352                         b.second.str) == 0);
7353 }
7354 
operator ==(const Role_id & a,const Role_id & b)7355 bool operator==(const Role_id &a, const Role_id &b) {
7356   return ((a.user() == b.user()) && (a.host().length() == b.host().length()) &&
7357           (my_strcasecmp(system_charset_info, a.host().c_str(),
7358                          b.host().c_str()) == 0));
7359 }
7360 
operator <(const Auth_id_ref & a,const Auth_id_ref & b)7361 bool operator<(const Auth_id_ref &a, const Auth_id_ref &b) {
7362   if (a.first.length != b.first.length) return a.first.length < b.first.length;
7363   if (a.second.length != b.second.length)
7364     return a.second.length < b.second.length;
7365 
7366   int first = memcmp(a.first.str, b.first.str, a.first.length);
7367   if (first != 0) return first < 0;
7368   int second = memcmp(a.second.str, b.second.str, a.second.length);
7369   if (second != 0) return second < 0;
7370   return false;
7371 }
7372 
operator ==(std::pair<const Role_id,std::pair<std::string,bool>> & a,const std::string & b)7373 bool operator==(std::pair<const Role_id, std::pair<std::string, bool>> &a,
7374                 const std::string &b) {
7375   return a.second.first == b;
7376 }
7377 
operator ==(const LEX_CSTRING & a,const LEX_CSTRING & b)7378 bool operator==(const LEX_CSTRING &a, const LEX_CSTRING &b) {
7379   return (a.length == b.length &&
7380           ((a.length == 0) || (memcmp(a.str, b.str, a.length) == 0)));
7381 }
7382 
7383 /**
7384   Checks if current user needs to be changed in case it is same as the LEX_USER.
7385   This check is useful to take backup of security context in case current user
7386   renames itself.
7387 
7388   @param sctx           The security context to check
7389   @param from_user_ptr  User name to be renamed
7390 
7391   @retval true          security context need to be updated
7392   @retval false         otherwise
7393 */
do_update_sctx(Security_context * sctx,LEX_USER * from_user_ptr)7394 bool do_update_sctx(Security_context *sctx, LEX_USER *from_user_ptr) {
7395   const char *sctx_user = sctx->priv_user().str;
7396   const char *sctx_host = sctx->priv_host().str;
7397   const char *from_user = from_user_ptr->user.str;
7398   const char *from_host = from_user_ptr->host.str;
7399 
7400   /* If the user is connected as a proxied user, verify against proxy user */
7401   if (sctx->proxy_user().str && *sctx->proxy_user().str != '\0') {
7402     sctx_user = sctx->user().str;
7403   }
7404 
7405   /* Update the security context if current_user is going to be changed. */
7406   if (strcmp(from_user, sctx_user) == 0 &&
7407       my_strcasecmp(system_charset_info, from_host, sctx_host) == 0) {
7408     return true;
7409   }
7410   return false;
7411 }
7412 
update_sctx(Security_context * sctx,LEX_USER * to_user_ptr)7413 void update_sctx(Security_context *sctx, LEX_USER *to_user_ptr) {
7414   const char *to_user = to_user_ptr->user.str;
7415   const char *to_host = to_user_ptr->host.str;
7416   if (!to_host) to_host = "";
7417   if (!to_user) to_user = "";
7418 
7419   sctx->assign_priv_user(to_user_ptr->user.str, to_user_ptr->user.length);
7420   sctx->assign_priv_host(to_user_ptr->host.str, to_user_ptr->host.length);
7421 }
7422 
7423 /**
7424   Checks if any of the users has SYSTEM_USER privilege then current user
7425   must also have SYSTEM_USER privilege.
7426   It is a wrapper over the  Privilege_checker class that does
7427   privilege checks for one user at a time.
7428 
7429   @param [in] thd  Thread handle for security context
7430   @param [in] list List of user being processed
7431 
7432   @returns If needed, whether current user has SYSTEM_USER privilege or not
7433     @retval false Either none of the users in list has SYSTEM_USER
7434                   privilege or current user has SYSTEM_USER privilege
7435     @retval true  Failed in get_current_user() OR one of the user in the
7436                   list has SYSTEM_USER privilege but current user does not.
7437 */
check_system_user_privilege(THD * thd,List<LEX_USER> list)7438 bool check_system_user_privilege(THD *thd, List<LEX_USER> list) {
7439   LEX_USER *user, *tmp_user;
7440   Security_context *sctx = thd->security_context();
7441   List_iterator<LEX_USER> user_list(list);
7442   DBUG_ASSERT(assert_acl_cache_read_lock(thd));
7443   if (list.size() == 0) return (false);
7444   while ((tmp_user = user_list++)) {
7445     if (!(user = get_current_user(thd, tmp_user))) {
7446       my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
7447                consts::system_user.c_str());
7448       return (true);
7449     }
7450     if (sctx->can_operate_with({user}, consts::system_user)) return (true);
7451   }
7452   return (false);
7453 }
7454