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