1 /* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
2    This program is free software; you can redistribute it and/or modify
3    it under the terms of the GNU General Public License, version 2.0,
4    as published by the Free Software Foundation.
5 
6    This program is also distributed with certain software (including
7    but not limited to OpenSSL) that is licensed under separate terms,
8    as designated in a particular file or component or in included license
9    documentation.  The authors of MySQL hereby grant you an additional
10    permission to link the program and your derivative works with the
11    separately licensed software that they have included with MySQL.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License, version 2.0, for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
21 
22 #include "sql/auth/sql_security_ctx.h"
23 
24 #include <map>
25 #include <string>
26 #include <tuple>
27 #include <unordered_map>
28 #include <unordered_set>
29 #include <utility>
30 #include <vector>
31 
32 #include "m_ctype.h"
33 #include "my_dbug.h"
34 #include "my_inttypes.h"
35 #include "my_sys.h"
36 #include "mysql/mysql_lex_string.h"
37 #include "mysql/psi/psi_base.h"
38 #include "mysql/service_mysql_alloc.h"
39 #include "mysqld_error.h"
40 #include "sql/auth/auth_acls.h"
41 #include "sql/auth/auth_common.h"
42 #include "sql/auth/auth_internal.h"
43 #include "sql/auth/sql_auth_cache.h"
44 #include "sql/auth/sql_authorization.h"
45 #include "sql/current_thd.h"
46 #include "sql/mysqld.h"
47 #include "sql/sql_class.h"
48 #include "sql/table.h"
49 
50 extern bool initialized;
51 
Security_context(THD * thd)52 Security_context::Security_context(THD *thd /*= nullptr */)
53     : m_restrictions(nullptr), m_thd(thd) {
54   init();
55 }
56 
Security_context(MEM_ROOT * mem_root,THD * thd)57 Security_context::Security_context(MEM_ROOT *mem_root, THD *thd /* = nullptr*/)
58     : m_restrictions(mem_root), m_thd(thd) {
59   init();
60 }
61 
~Security_context()62 Security_context::~Security_context() { destroy(); }
63 
Security_context(const Security_context & src_sctx)64 Security_context::Security_context(const Security_context &src_sctx)
65     : m_restrictions(nullptr), m_thd(nullptr) {
66   copy_security_ctx(src_sctx);
67 }
68 
operator =(const Security_context & src_sctx)69 Security_context &Security_context::operator=(
70     const Security_context &src_sctx) {
71   DBUG_TRACE;
72 
73   if (this != &src_sctx) {
74     destroy();
75     copy_security_ctx(src_sctx);
76   }
77 
78   return *this;
79 }
80 
init()81 void Security_context::init() {
82   DBUG_TRACE;
83 
84   m_user.set((const char *)nullptr, 0, system_charset_info);
85   m_host.set("", 0, system_charset_info);
86   m_ip.set("", 0, system_charset_info);
87   m_host_or_ip.set(STRING_WITH_LEN("connecting host"), system_charset_info);
88   m_external_user.set("", 0, system_charset_info);
89   m_priv_user[0] = m_priv_host[0] = m_proxy_user[0] = '\0';
90   m_priv_user_length = m_priv_host_length = m_proxy_user_length = 0;
91   m_master_access = 0;
92   m_db_access = NO_ACCESS;
93   m_acl_map = nullptr;
94   m_map_checkout_count = 0;
95   m_password_expired = false;
96   m_is_locked = false;
97   m_is_skip_grants_user = false;
98   m_has_drop_policy = false;
99   m_executed_drop_policy = false;
100 }
101 
logout()102 void Security_context::logout() {
103   if (m_acl_map) {
104     DBUG_PRINT("info",
105                ("(logout) Security_context for %s@%s returns Acl_map to cache. "
106                 "Map reference count= %u",
107                 m_user.c_ptr(), m_host.c_ptr(), m_acl_map->reference_count()));
108     get_global_acl_cache()->return_acl_map(m_acl_map);
109     m_acl_map = nullptr;
110     clear_active_roles();
111     clear_db_restrictions();
112   }
113 }
114 
has_drop_policy(void)115 bool Security_context::has_drop_policy(void) { return m_has_drop_policy; }
116 
has_executed_drop_policy(void)117 bool Security_context::has_executed_drop_policy(void) {
118   return m_executed_drop_policy;
119 }
120 
execute_drop_policy(void)121 void Security_context::execute_drop_policy(void) {
122   if (m_has_drop_policy && !m_executed_drop_policy) {
123     (*m_drop_policy)(this);
124     m_executed_drop_policy = true;
125   }
126 }
127 
set_drop_policy(const std::function<void (Security_context *)> & func)128 void Security_context::set_drop_policy(
129     const std::function<void(Security_context *)> &func) {
130   m_drop_policy.reset(new std::function<void(Security_context *)>(func));
131   m_has_drop_policy = true;
132   m_executed_drop_policy = false;
133 }
134 
destroy()135 void Security_context::destroy() {
136   DBUG_TRACE;
137   execute_drop_policy();
138   if (m_acl_map) {
139     DBUG_PRINT(
140         "info",
141         ("(destroy) Security_context for %s@%s returns Acl_map to cache. "
142          "Map reference count= %u",
143          m_user.c_ptr(), m_host.c_ptr(), m_acl_map->reference_count()));
144     get_global_acl_cache()->return_acl_map(m_acl_map);
145     clear_active_roles();
146   }
147   m_acl_map = nullptr;
148   if (m_user.length())
149     m_user.set((const char *)nullptr, 0, system_charset_info);
150 
151   if (m_host.length()) m_host.set("", 0, system_charset_info);
152 
153   if (m_ip.length()) m_ip.set("", 0, system_charset_info);
154 
155   if (m_host_or_ip.length()) m_host_or_ip.set("", 0, system_charset_info);
156 
157   if (m_external_user.length()) m_external_user.set("", 0, system_charset_info);
158 
159   m_priv_user[0] = m_priv_host[0] = m_proxy_user[0] = 0;
160   m_priv_user_length = m_priv_host_length = m_proxy_user_length = 0;
161 
162   m_master_access = m_db_access = 0;
163   m_password_expired = false;
164   m_is_skip_grants_user = false;
165   clear_db_restrictions();
166 }
167 
168 /**
169   Grants all privilegs to user. Sets the user and host name of privilege user.
170 
171   @param[in]  user User name for current_user to set.
172                    Default value is "skip-grants user"
173   @param[in]  host Host name for the current user to set.
174                    Default value is "skip-grants host"
175 */
skip_grants(const char * user,const char * host)176 void Security_context::skip_grants(const char *user /*= "skip-grants user"*/,
177                                    const char *host /*= "skip-grants host"*/) {
178   DBUG_TRACE;
179 
180   /* privileges for the user are unknown everything is allowed */
181   set_host_or_ip_ptr("", 0);
182   assign_priv_user(user, strlen(user));
183   assign_priv_host(host, strlen(host));
184   m_master_access = ~NO_ACCESS;
185   m_is_skip_grants_user = true;
186 
187   /*
188     If the security context is tied upto to the THD object and it is
189     current security context in THD then set the flag to true.
190   */
191   if (m_thd && m_thd->security_context() == this) {
192     m_thd->set_system_user(true);
193   }
194 }
195 
196 /**
197   Deep copy status of sctx object to this.
198 
199   @param[in]    src_sctx   Object from which status should be copied.
200 */
201 
copy_security_ctx(const Security_context & src_sctx)202 void Security_context::copy_security_ctx(const Security_context &src_sctx) {
203   DBUG_TRACE;
204 
205   assign_user(src_sctx.m_user.ptr(), src_sctx.m_user.length());
206   assign_host(src_sctx.m_host.ptr(), src_sctx.m_host.length());
207   assign_ip(src_sctx.m_ip.ptr(), src_sctx.m_ip.length());
208   if (!strcmp(src_sctx.m_host_or_ip.ptr(), my_localhost))
209     set_host_or_ip_ptr(my_localhost, strlen(my_localhost));
210   else
211     set_host_or_ip_ptr();
212   assign_external_user(src_sctx.m_external_user.ptr(),
213                        src_sctx.m_external_user.length());
214   assign_priv_user(src_sctx.m_priv_user, src_sctx.m_priv_user_length);
215   assign_proxy_user(src_sctx.m_proxy_user, src_sctx.m_proxy_user_length);
216   assign_priv_host(src_sctx.m_priv_host, src_sctx.m_priv_host_length);
217   m_db_access = src_sctx.m_db_access;
218   m_master_access = src_sctx.m_master_access;
219   m_password_expired = src_sctx.m_password_expired;
220   m_acl_map =
221       nullptr;  // acl maps are reference counted we can't copy or share them!
222   m_has_drop_policy = false;  // you cannot copy a drop policy
223   m_executed_drop_policy = false;
224   m_restrictions = src_sctx.restrictions();
225 }
226 
227 /**
228   Initialize this security context from the passed in credentials
229   and activate it in the current thread.
230 
231   @param       thd           Thread handle.
232   @param       definer_user  user part of a 'definer' value.
233   @param       definer_host  host part of a 'definer' value.
234   @param       db            Database name.
235   @param[out]  backup        Save a pointer to the current security context
236                              in the thread. In case of success it points to the
237                              saved old context, otherwise it points to NULL.
238   @param       force         Force context switch
239 
240 
241   @note The Security_context_factory should be used as a replacement to this
242     function at every opportunity.
243 
244   During execution of a statement, multiple security contexts may
245   be needed:
246   - the security context of the authenticated user, used as the
247     default security context for all top-level statements
248   - in case of a view or a stored program, possibly the security
249     context of the definer of the routine, if the object is
250     defined with SQL SECURITY DEFINER option.
251 
252   The currently "active" security context is parameterized in THD
253   member security_ctx. By default, after a connection is
254   established, this member points at the "main" security context
255   - the credentials of the authenticated user.
256 
257   Later, if we would like to execute some sub-statement or a part
258   of a statement under credentials of a different user, e.g.
259   definer of a procedure, we authenticate this user in a local
260   instance of Security_context by means of this method (and
261   ultimately by means of acl_getroot), and make the
262   local instance active in the thread by re-setting
263   thd->m_security_ctx pointer.
264 
265   Note, that the life cycle and memory management of the "main" and
266   temporary security contexts are different.
267   For the main security context, the memory for user/host/ip is
268   allocated on system heap, and the THD class frees this memory in
269   its destructor. The only case when contents of the main security
270   context may change during its life time is when someone issued
271   CHANGE USER command.
272   Memory management of a "temporary" security context is
273   responsibility of the module that creates it.
274 
275   @retval true  there is no user with the given credentials. The erro
276                 is reported in the thread.
277   @retval false success
278 */
279 
change_security_context(THD * thd,const LEX_CSTRING & definer_user,const LEX_CSTRING & definer_host,const char * db,Security_context ** backup,bool force)280 bool Security_context::change_security_context(
281     THD *thd, const LEX_CSTRING &definer_user, const LEX_CSTRING &definer_host,
282     const char *db, Security_context **backup, bool force) {
283   bool needs_change;
284 
285   DBUG_TRACE;
286 
287   DBUG_ASSERT(definer_user.str && definer_host.str);
288 
289   *backup = nullptr;
290   needs_change =
291       (strcmp(definer_user.str, thd->security_context()->priv_user().str) ||
292        my_strcasecmp(system_charset_info, definer_host.str,
293                      thd->security_context()->priv_host().str));
294   if (needs_change || force) {
295     if (acl_getroot(thd, this, definer_user.str, definer_host.str,
296                     definer_host.str, db)) {
297       my_error(ER_NO_SUCH_USER, MYF(0), definer_user.str, definer_host.str);
298       return true;
299     }
300     *backup = thd->security_context();
301     thd->set_security_context(this);
302   }
303 
304   return false;
305 }
306 
restore_security_context(THD * thd,Security_context * backup)307 void Security_context::restore_security_context(THD *thd,
308                                                 Security_context *backup) {
309   if (backup) thd->set_security_context(backup);
310 }
311 
user_matches(Security_context * them)312 bool Security_context::user_matches(Security_context *them) {
313   DBUG_TRACE;
314 
315   const char *them_user = them->user().str;
316 
317   return (m_user.ptr() != nullptr) && (them_user != nullptr) &&
318          !strcmp(m_user.ptr(), them_user);
319 }
320 
check_access(ulong want_access,const std::string & db_name,bool match_any)321 bool Security_context::check_access(ulong want_access,
322                                     const std::string &db_name /* = "" */,
323                                     bool match_any) {
324   DBUG_TRACE;
325   if ((want_access & DB_ACLS) &&
326       (is_access_restricted_on_db(want_access, db_name))) {
327     return false;
328   }
329   return (match_any ? (m_master_access & want_access)
330                     : ((m_master_access & want_access) == want_access));
331 }
332 
master_access(const std::string & db_name) const333 ulong Security_context::master_access(const std::string &db_name) const {
334   return filter_access(m_master_access, db_name);
335 }
336 
337 /**
338   This method pushes a role to the list of active roles. It requires
339   Acl_cache_lock_guard.
340 
341   This method allocates memory which must be freed when the role is
342   deactivated.
343 
344   @param role The role name
345   @param role_host The role hostname-part.
346   @param validate_access True if access validation should be performed.
347     Default value is false.
348 */
activate_role(LEX_CSTRING role,LEX_CSTRING role_host,bool validate_access)349 int Security_context::activate_role(LEX_CSTRING role, LEX_CSTRING role_host,
350                                     bool validate_access) {
351   auto res = std::find(m_active_roles.begin(), m_active_roles.end(),
352                        create_authid_from(role, role_host));
353   /* silently ignore requests of activating an already active role */
354   if (res != m_active_roles.end()) return 0;
355   LEX_CSTRING dup_role = {
356       my_strdup(PSI_NOT_INSTRUMENTED, role.str, MYF(MY_WME)), role.length};
357   LEX_CSTRING dup_role_host = {
358       my_strdup(PSI_NOT_INSTRUMENTED, role_host.str, MYF(MY_WME)),
359       role_host.length};
360   if (validate_access && !check_if_granted_role(priv_user(), priv_host(),
361                                                 dup_role, dup_role_host)) {
362     my_free(const_cast<char *>(dup_role.str));
363     my_free(const_cast<char *>(dup_role_host.str));
364     return ER_ACCESS_DENIED_ERROR;
365   }
366   m_active_roles.push_back(std::make_pair(dup_role, dup_role_host));
367   return 0;
368 }
369 
370 /**
371   Subscribes to a cache entry of aggregated ACLs.
372   A Security_context can only have one subscription at a time. If another one
373   is requested, the former will be returned.
374 
375   We do this subscription before execution of every statement(prepared or
376   conventional) as the global acl version might have increased due to
377   a grant/revoke or flush. Hence, the granularity of after effects of
378   grant/revoke or flush due to roles is per statement.
379 */
checkout_access_maps(void)380 void Security_context::checkout_access_maps(void) {
381   DBUG_TRACE;
382 
383   /*
384     If we're checkout out a map before we return it now, because we're only
385     allowed to have one map at a time.
386     However, if we've just authenticated we don't need to checkout a new map
387     so we check if there has been any previous checkouts.
388   */
389   if (m_acl_map != nullptr) {
390     DBUG_PRINT(
391         "info",
392         ("(checkout) Security_context for %.*s@%.*s returns Acl_map to cache. "
393          "Map reference count= %u",
394          (int)m_priv_user_length, m_priv_user, (int)m_priv_host_length,
395          m_priv_host, m_acl_map->reference_count()));
396     get_global_acl_cache()->return_acl_map(m_acl_map);
397     m_acl_map = nullptr;
398   }
399 
400   if (m_active_roles.size() == 0) return;
401   ++m_map_checkout_count;
402   Auth_id_ref uid;
403   uid.first.str = this->m_priv_user;
404   uid.first.length = this->m_priv_user_length;
405   uid.second.str = this->m_priv_host;
406   uid.second.length = this->m_priv_host_length;
407   m_acl_map =
408       get_global_acl_cache()->checkout_acl_map(this, uid, m_active_roles);
409   if (m_acl_map != nullptr) {
410     DBUG_PRINT("info",
411                ("Roles are active and global access for %.*s@%.*s is set to"
412                 " %lu",
413                 (int)m_priv_user_length, m_priv_user, (int)m_priv_host_length,
414                 m_priv_host, m_acl_map->global_acl()));
415     set_master_access(m_acl_map->global_acl(), m_acl_map->restrictions());
416   } else {
417     set_master_access(0);
418   }
419 }
420 
421 /**
422   This helper method clears the active roles list and frees the allocated
423   memory used for any previously activated roles.
424 */
clear_active_roles(void)425 void Security_context::clear_active_roles(void) {
426   for (List_of_auth_id_refs::iterator it = m_active_roles.begin();
427        it != m_active_roles.end(); ++it) {
428     my_free(const_cast<char *>(it->first.str));
429     it->first.str = nullptr;
430     it->first.length = 0;
431     my_free(const_cast<char *>(it->second.str));
432     it->second.str = nullptr;
433     it->second.length = 0;
434   }
435   m_active_roles.clear();
436   /*
437     Clear does not actually free the memory as an optimization for reuse.
438     This confuses valgrind, so we swap with an empty vector to ensure the
439     memory is freed when testing with valgrind
440   */
441   List_of_auth_id_refs().swap(m_active_roles);
442 }
443 
get_active_roles(void)444 List_of_auth_id_refs *Security_context::get_active_roles(void) {
445   return &m_active_roles;
446 }
447 
get_num_active_roles(void) const448 size_t Security_context::get_num_active_roles(void) const {
449   return m_active_roles.size();
450 }
451 
452 /**
453   Get sorted list of roles in LEX_USER format
454 
455   @param [in]  thd  For mem_root
456   @param [out] list List of active roles
457 */
get_active_roles(THD * thd,List<LEX_USER> & list)458 void Security_context::get_active_roles(THD *thd, List<LEX_USER> &list) {
459   List_of_granted_roles roles;
460   for (const auto &role : m_active_roles) {
461     roles.push_back(std::make_pair(Role_id(role.first, role.second), false));
462   }
463   if (roles.size()) std::sort(roles.begin(), roles.end());
464   for (const auto &role : roles) {
465     LEX_STRING user, host;
466     user.str = strmake_root(thd->mem_root, role.first.user().c_str(),
467                             role.first.user().length());
468     user.length = role.first.user().length();
469     host.str = strmake_root(thd->mem_root, role.first.host().c_str(),
470                             role.first.host().length());
471     host.length = role.first.host().length();
472     LEX_USER *user_lex = LEX_USER::alloc(thd, &user, &host);
473     list.push_back(user_lex);
474   }
475 }
476 
db_acl(LEX_CSTRING db,bool use_pattern_scan) const477 ulong Security_context::db_acl(LEX_CSTRING db, bool use_pattern_scan) const {
478   DBUG_TRACE;
479   if (m_acl_map == nullptr || db.length == 0) return 0;
480 
481   std::string key(db.str, db.length);
482   Db_access_map::iterator found_acl_it = m_acl_map->db_acls()->find(key);
483   if (found_acl_it == m_acl_map->db_acls()->end()) {
484     if (use_pattern_scan) {
485       Db_access_map::iterator it = m_acl_map->db_wild_acls()->begin();
486       ulong access = 0;
487       for (; it != m_acl_map->db_wild_acls()->end(); ++it) {
488         /*
489           Do the usual string comparision if partial_revokes is ON,
490           otherwise do the wildcard grant comparision
491         */
492         if (mysqld_partial_revokes()
493                 ? (my_strcasecmp(system_charset_info, db.str,
494                                  it->first.c_str()) == 0)
495                 : (wild_case_compare(system_charset_info, db.str, db.length,
496                                      it->first.c_str(),
497                                      it->first.size()) == 0)) {
498           DBUG_PRINT("info", ("Found matching db pattern %s for key %s",
499                               it->first.c_str(), key.c_str()));
500           access |= it->second;
501         }
502       }
503       return filter_access(access, key);
504     } else {
505       DBUG_PRINT("info", ("Db %s not found in cache (no pattern matching)",
506                           key.c_str()));
507       return 0;
508     }
509   } else {
510     DBUG_PRINT("info", ("Found exact match for db %s", key.c_str()));
511     return filter_access(found_acl_it->second, key);
512   }
513 }
514 
procedure_acl(LEX_CSTRING db,LEX_CSTRING procedure_name)515 ulong Security_context::procedure_acl(LEX_CSTRING db,
516                                       LEX_CSTRING procedure_name) {
517   if (m_acl_map == nullptr)
518     return 0;
519   else {
520     SP_access_map::iterator it;
521     String q_name;
522     append_identifier(&q_name, db.str, db.length);
523     q_name.append(".");
524     std::string name(procedure_name.str, procedure_name.length);
525     my_casedn_str(files_charset_info, &name[0]);
526     append_identifier(&q_name, name.c_str(), name.length());
527     it = m_acl_map->sp_acls()->find(q_name.c_ptr());
528     if (it == m_acl_map->sp_acls()->end()) return 0;
529     return filter_access(it->second, q_name.c_ptr());
530   }
531 }
532 
function_acl(LEX_CSTRING db,LEX_CSTRING func_name)533 ulong Security_context::function_acl(LEX_CSTRING db, LEX_CSTRING func_name) {
534   if (m_acl_map == nullptr)
535     return 0;
536   else {
537     String q_name;
538     append_identifier(&q_name, db.str, db.length);
539     q_name.append(".");
540     std::string name(func_name.str, func_name.length);
541     my_casedn_str(files_charset_info, &name[0]);
542     append_identifier(&q_name, name.c_str(), name.length());
543     SP_access_map::iterator it;
544     it = m_acl_map->func_acls()->find(q_name.c_ptr());
545     if (it == m_acl_map->func_acls()->end()) return 0;
546     return filter_access(it->second, q_name.c_ptr());
547   }
548 }
549 
550 // return the entire element instead of just the acl?
table_and_column_acls(LEX_CSTRING db,LEX_CSTRING table)551 Grant_table_aggregate Security_context::table_and_column_acls(
552     LEX_CSTRING db, LEX_CSTRING table) {
553   if (m_acl_map == nullptr) return Grant_table_aggregate();
554   Table_access_map::iterator it;
555   String q_name;
556   append_identifier(&q_name, db.str, db.length);
557   q_name.append(".");
558   append_identifier(&q_name, table.str, table.length);
559   it = m_acl_map->table_acls()->find(std::string(q_name.c_ptr_quick()));
560   if (it == m_acl_map->table_acls()->end()) return Grant_table_aggregate();
561   return it->second;
562 }
563 
table_acl(LEX_CSTRING db,LEX_CSTRING table)564 ulong Security_context::table_acl(LEX_CSTRING db, LEX_CSTRING table) {
565   if (m_acl_map == nullptr) return 0;
566   Grant_table_aggregate aggr = table_and_column_acls(db, table);
567   return filter_access(aggr.table_access, db.str ? db.str : "");
568 }
569 
has_with_admin_acl(const LEX_CSTRING & role_name,const LEX_CSTRING & role_host)570 bool Security_context::has_with_admin_acl(const LEX_CSTRING &role_name,
571                                           const LEX_CSTRING &role_host) {
572   DBUG_TRACE;
573   if (m_acl_map == nullptr) return false;
574   String q_name;
575   append_identifier(&q_name, role_name.str, role_name.length);
576   q_name.append("@");
577   append_identifier(&q_name, role_host.str, role_host.length);
578   Grant_acl_set::iterator it =
579       m_acl_map->grant_acls()->find(std::string(q_name.c_ptr_quick()));
580   if (it != m_acl_map->grant_acls()->end()) return true;
581   return false;
582 }
583 
any_sp_acl(const LEX_CSTRING & db)584 bool Security_context::any_sp_acl(const LEX_CSTRING &db) {
585   if ((db_acl(db, true) & PROC_ACLS) != 0) return true;
586   SP_access_map::iterator it = m_acl_map->sp_acls()->begin();
587   for (; it != m_acl_map->sp_acls()->end(); ++it) {
588     String id_db;
589     append_identifier(&id_db, db.str, db.length);
590     if (it->first.compare(0, id_db.length(), id_db.c_ptr(), id_db.length()) ==
591         0) {
592       /* There's at least one SP with grants for this db */
593       return true;
594     }
595   }
596   return false;
597 }
598 
any_table_acl(const LEX_CSTRING & db)599 bool Security_context::any_table_acl(const LEX_CSTRING &db) {
600   if ((db_acl(db, true) & TABLE_ACLS) != 0) return true;
601   Table_access_map::iterator table_it = m_acl_map->table_acls()->begin();
602   for (; table_it != m_acl_map->table_acls()->end(); ++table_it) {
603     String id_db;
604     append_identifier(&id_db, db.str, db.length);
605     if (table_it->first.compare(0, id_db.length(), id_db.c_ptr(),
606                                 id_db.length()) == 0) {
607       /* There's at least one table with grants for this db*/
608       return true;
609     }
610   }
611   return false;
612 }
613 
614 /**
615   Checks if the Current_user has the asked dynamic privilege.
616 
617   if the server is initializing the datadir, or current_user is
618   --skip-grants-user then it returns that user has privilege with
619   grant option.
620 
621   @param [in] priv      privilege to check
622   @param [in] priv_len  length of privilege
623 
624   @returns  pair/<has_privilege, has_with_grant_option/>
625     @retval /<true, true/>  has required privilege with grant option
626     @retval /<true, false/> has required privilege without grant option
627     @retval /<false, false/> does not have the required privilege
628 */
has_global_grant(const char * priv,size_t priv_len)629 std::pair<bool, bool> Security_context::has_global_grant(const char *priv,
630                                                          size_t priv_len) {
631   /* server started with --skip-grant-tables */
632   if (!initialized || m_is_skip_grants_user) return std::make_pair(true, true);
633 
634   std::string privilege(priv, priv_len);
635 
636   if (m_acl_map == nullptr) {
637     THD *thd = m_thd ? m_thd : current_thd;
638     if (thd == nullptr) {
639       DBUG_PRINT("error", ("Security Context must have valid THD handle to"
640                            " probe grants.\n"));
641       return {false, false};
642     }
643     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
644     if (!acl_cache_lock.lock(false)) return std::make_pair(false, false);
645     Role_id key(&m_priv_user[0], m_priv_user_length, &m_priv_host[0],
646                 m_priv_host_length);
647     User_to_dynamic_privileges_map::iterator it, it_end;
648     std::tie(it, it_end) = get_dynamic_privileges_map()->equal_range(key);
649     it = std::find(it, it_end, privilege);
650     if (it != it_end) {
651       return std::make_pair(true, it->second.second);
652     }
653     return std::make_pair(false, false);
654   }
655   Dynamic_privileges::iterator it =
656       m_acl_map->dynamic_privileges()->find(privilege);
657   if (it != m_acl_map->dynamic_privileges()->end()) {
658     return std::make_pair(true, it->second);
659   }
660 
661   return std::make_pair(false, false);
662 }
663 
664 /**
665   Checks if the Auth_id have the asked dynamic privilege.
666 
667   @param [in] auth_id     Auth_id that could represent either a user or a role
668   @param [in] privilege   privilege to check for
669   @param [in] cumulative  Flag to decide how to fetch the privileges of ACL_USER
670                           false - privilege granted directly or set through
671                                   a role
672                           true  - privileges granted directly or coming through
673                                   roles granted to it irrespective the roles are
674                                   active or not.
675 
676   @returns  pair/<has_privilege, has_with_grant_option/>
677     @retval /<true, true/>  has required privilege with grant option
678     @retval /<true, false/> has required privilege without grant option
679     @retval /<false, false/> does not have the required privilege, OR
680                              auth_id does not exist.
681 */
has_global_grant(const Auth_id & auth_id,const std::string & privilege,bool cumulative)682 std::pair<bool, bool> Security_context::has_global_grant(
683     const Auth_id &auth_id, const std::string &privilege,
684     bool cumulative /*= false*/) {
685   std::pair<bool, bool> has_privilege{false, false};
686   Acl_cache_lock_guard acl_cache_lock(current_thd,
687                                       Acl_cache_lock_mode::READ_MODE);
688   if (!acl_cache_lock.lock(false)) return has_privilege;
689 
690   ACL_USER *acl_user =
691       find_acl_user(auth_id.host().c_str(), auth_id.user().c_str(), true);
692   if (!acl_user) return has_privilege;
693 
694   return fetch_global_grant(*acl_user, privilege, cumulative);
695 }
696 
697 /**
698   Checks if the specified auth_id with privilege can work with the current_user.
699   If the auth_id has the specified privilege then current_user must also have
700   the same privilege. Throws error is the auth_id has the privilege but
701   current_user does not have it.
702 
703   @param  [in]  auth_id     Auth_id that could represent either a user or a role
704   @param  [in]  privilege   Privilege to check for mismatch
705   @param  [in]  cumulative  Flag to decide how to check the privileges of
706                             auth_id
707                             false - privilege granted directly or set
708                                     through a role
709                             true  - privileges granted directly or coming
710                                     through roles granted to it irrespective
711                                     the roles are active or not.
712   @param  [in]  ignore_if_nonextant Flag to decide how to treat the non-existing
713                                     auth_id.
714                                     true  - consider as privilege exists
715                                     false - consider as privilege do not exist
716 
717   @retval true    auth_id has the privilege but the current_auth does not, also
718                   throws error.
719   @retval false   Otherwise
720 */
can_operate_with(const Auth_id & auth_id,const std::string & privilege,bool cumulative,bool ignore_if_nonextant)721 bool Security_context::can_operate_with(const Auth_id &auth_id,
722                                         const std::string &privilege,
723                                         bool cumulative /*= false */,
724                                         bool ignore_if_nonextant /*= true */) {
725   DBUG_TRACE;
726   Acl_cache_lock_guard acl_cache_lock(current_thd,
727                                       Acl_cache_lock_mode::READ_MODE);
728   if (!acl_cache_lock.lock()) {
729     DBUG_PRINT("error", ("Could not check for the SYSTEM_USER privilege. "
730                          "Could not lock Acl caches.\n"));
731     return true;
732   }
733   ACL_USER *acl_user =
734       find_acl_user(auth_id.host().c_str(), auth_id.user().c_str(), true);
735   if (!acl_user) {
736     if (ignore_if_nonextant)
737       return false;
738     else {
739       my_error(ER_USER_DOES_NOT_EXIST, MYF(0), auth_id.auth_str().c_str());
740       return true;
741     }
742   }
743 
744   bool is_mismatch = false;
745   if (fetch_global_grant(*acl_user, privilege, cumulative).first) {
746     is_mismatch = has_global_grant(privilege.c_str(), privilege.length()).first
747                       ? false
748                       : true;
749   }
750   if (is_mismatch) {
751     my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), privilege.c_str());
752   }
753   return is_mismatch;
754 }
755 
priv_user() const756 LEX_CSTRING Security_context::priv_user() const {
757   LEX_CSTRING priv_user;
758   DBUG_TRACE;
759   priv_user.str = m_priv_user;
760   priv_user.length = m_priv_user_length;
761   return priv_user;
762 }
763 
764 /**
765   Getter method for member m_user.
766 
767   @retval LEX_CSTRING object having constant pointer to m_user.Ptr
768           and its length.
769 */
user() const770 LEX_CSTRING Security_context::user() const {
771   LEX_CSTRING user;
772 
773   DBUG_TRACE;
774 
775   user.str = m_user.ptr();
776   user.length = m_user.length();
777 
778   return user;
779 }
780 
781 /**
782   Setter method for member m_user.
783   Function just sets the user_arg pointer value to the
784   m_user, user_arg value is *not* copied.
785 
786   @param[in]    user_arg         New user value for m_user.
787   @param[in]    user_arg_length  Length of "user_arg" param.
788 */
789 
set_user_ptr(const char * user_arg,const size_t user_arg_length)790 void Security_context::set_user_ptr(const char *user_arg,
791                                     const size_t user_arg_length) {
792   DBUG_TRACE;
793 
794   if (user_arg == m_user.ptr()) return;
795 
796   // set new user value to m_user.
797   m_user.set(user_arg, user_arg_length, system_charset_info);
798 }
799 
800 /**
801   Setter method for member m_user.
802 
803   Copies user_arg value to the m_user if it is not null else m_user is set
804   to NULL.
805 
806   @param[in]    user_arg         New user value for m_user.
807   @param[in]    user_arg_length  Length of "user_arg" param.
808 */
809 
assign_user(const char * user_arg,const size_t user_arg_length)810 void Security_context::assign_user(const char *user_arg,
811                                    const size_t user_arg_length) {
812   DBUG_TRACE;
813 
814   if (user_arg == m_user.ptr()) return;
815 
816   if (user_arg)
817     m_user.copy(user_arg, user_arg_length, system_charset_info);
818   else
819     m_user.set((const char *)nullptr, 0, system_charset_info);
820 }
821 
822 /**
823   Getter method for member m_host.
824 
825   @retval LEX_CSTRING object having constant pointer to m_host.Ptr
826           and its length.
827 */
host() const828 LEX_CSTRING Security_context::host() const {
829   LEX_CSTRING host;
830 
831   DBUG_TRACE;
832 
833   host.str = m_host.ptr();
834   host.length = m_host.length();
835 
836   return host;
837 }
838 
839 /**
840   Setter method for member m_host.
841   Function just sets the host_arg pointer value to the
842   m_host, host_arg value is *not* copied.
843   host_arg value must not be NULL.
844 
845   @param[in]    host_arg         New user value for m_host.
846   @param[in]    host_arg_length  Length of "host_arg" param.
847 */
set_host_ptr(const char * host_arg,const size_t host_arg_length)848 void Security_context::set_host_ptr(const char *host_arg,
849                                     const size_t host_arg_length) {
850   DBUG_TRACE;
851 
852   DBUG_ASSERT(host_arg != nullptr);
853 
854   if (host_arg == m_host.ptr()) return;
855 
856   // set new host value to m_host.
857   m_host.set(host_arg, host_arg_length, system_charset_info);
858 }
859 
860 /**
861   Setter method for member m_host.
862 
863   Copies host_arg value to the m_host if it is not null else m_user is set
864   to empty string.
865 
866 
867   @param[in]    host_arg         New user value for m_host.
868   @param[in]    host_arg_length  Length of "host_arg" param.
869 */
870 
assign_host(const char * host_arg,const size_t host_arg_length)871 void Security_context::assign_host(const char *host_arg,
872                                    const size_t host_arg_length) {
873   DBUG_TRACE;
874 
875   if (host_arg == nullptr) {
876     m_host.set("", 0, system_charset_info);
877     goto end;
878   } else if (host_arg == m_host.ptr()) {
879     goto end;
880   } else if (*host_arg) {
881     m_host.copy(host_arg, host_arg_length, system_charset_info);
882     goto end;
883   }
884 
885 end:
886   return;
887 }
888 
889 /**
890   Getter method for member m_ip.
891 
892   @retval LEX_CSTRING object having constant pointer to m_ip.Ptr
893           and its length
894 */
ip() const895 LEX_CSTRING Security_context::ip() const {
896   LEX_CSTRING ip;
897 
898   DBUG_TRACE;
899 
900   ip.str = m_ip.ptr();
901   ip.length = m_ip.length();
902 
903   return ip;
904 }
905 
906 /**
907   Setter method for member m_ip.
908   Function just sets the ip_arg pointer value to the
909   m_ip, ip_arg value is *not* copied.
910 
911   @param[in]    ip_arg         New user value for m_ip.
912   @param[in]    ip_arg_length  Length of "ip_arg" param.
913 */
914 
set_ip_ptr(const char * ip_arg,const int ip_arg_length)915 void Security_context::set_ip_ptr(const char *ip_arg, const int ip_arg_length) {
916   DBUG_TRACE;
917 
918   if (ip_arg == m_ip.ptr()) return;
919 
920   // set new ip value to m_ip.
921   m_ip.set(ip_arg, ip_arg_length, system_charset_info);
922 }
923 
924 /**
925   Setter method for member m_ip.
926 
927   Copies ip_arg value to the m_ip if it is not null else m_ip is set
928   to NULL.
929 
930 
931   @param[in]    ip_arg         New user value for m_ip.
932   @param[in]    ip_arg_length  Length of "ip_arg" param.
933 */
934 
assign_ip(const char * ip_arg,const int ip_arg_length)935 void Security_context::assign_ip(const char *ip_arg, const int ip_arg_length) {
936   DBUG_TRACE;
937 
938   if (ip_arg == m_ip.ptr()) return;
939 
940   if (ip_arg)
941     m_ip.copy(ip_arg, ip_arg_length, system_charset_info);
942   else
943     m_ip.set((const char *)nullptr, 0, system_charset_info);
944 }
945 
946 /**
947   Setter method for member m_external_user.
948   Function just sets the ext_user_arg pointer to the
949   m_external_user, ext_user_arg is *not* copied.
950 
951   @param[in]    ext_user_arg         New user value for m_external_user.
952   @param[in]    ext_user_arg_length  Length of "ext_user_arg" param.
953 */
954 
set_external_user_ptr(const char * ext_user_arg,const int ext_user_arg_length)955 void Security_context::set_external_user_ptr(const char *ext_user_arg,
956                                              const int ext_user_arg_length) {
957   DBUG_TRACE;
958 
959   if (ext_user_arg == m_external_user.ptr()) return;
960 
961   // set new ip value to m_ip.
962   m_external_user.set(ext_user_arg, ext_user_arg_length, system_charset_info);
963 }
964 
965 /**
966   Setter method for member m_external_user.
967 
968   Copies ext_user_arg value to the m_external_user if it is not null
969   else m_external_user is set to NULL.
970 
971   @param[in]    ext_user_arg         New user value for m_external_user.
972   @param[in]    ext_user_arg_length  Length of "ext_user_arg" param.
973 */
974 
assign_external_user(const char * ext_user_arg,const int ext_user_arg_length)975 void Security_context::assign_external_user(const char *ext_user_arg,
976                                             const int ext_user_arg_length) {
977   DBUG_TRACE;
978 
979   if (ext_user_arg == m_external_user.ptr()) return;
980 
981   if (ext_user_arg)
982     m_external_user.copy(ext_user_arg, ext_user_arg_length,
983                          system_charset_info);
984   else
985     m_external_user.set((const char *)nullptr, 0, system_charset_info);
986 }
987 
988 /**
989   Setter method for member m_priv_user.
990 
991   @param[in]    priv_user_arg         New user value for m_priv_user.
992   @param[in]    priv_user_arg_length  Length of "priv_user_arg" param.
993 */
994 
assign_priv_user(const char * priv_user_arg,const size_t priv_user_arg_length)995 void Security_context::assign_priv_user(const char *priv_user_arg,
996                                         const size_t priv_user_arg_length) {
997   DBUG_TRACE;
998 
999   if (priv_user_arg_length) {
1000     m_priv_user_length =
1001         std::min(priv_user_arg_length, sizeof(m_priv_user) - 1);
1002     strmake(m_priv_user, priv_user_arg, m_priv_user_length);
1003   } else {
1004     *m_priv_user = 0;
1005     m_priv_user_length = 0;
1006   }
1007 }
1008 
1009 /**
1010   Getter method for member m_proxy_user.
1011 
1012   @retval LEX_CSTRING object having constant pointer to m_proxy_user.Ptr
1013           and its length
1014 */
proxy_user() const1015 LEX_CSTRING Security_context::proxy_user() const {
1016   LEX_CSTRING proxy_user;
1017 
1018   DBUG_TRACE;
1019 
1020   proxy_user.str = m_proxy_user;
1021   proxy_user.length = m_proxy_user_length;
1022 
1023   return proxy_user;
1024 }
1025 
1026 /**
1027   Setter method for member m_proxy_user.
1028 
1029   @param[in]    proxy_user_arg         New user value for m_proxy_user.
1030   @param[in]    proxy_user_arg_length  Length of "proxy_user_arg" param.
1031 */
1032 
assign_proxy_user(const char * proxy_user_arg,const size_t proxy_user_arg_length)1033 void Security_context::assign_proxy_user(const char *proxy_user_arg,
1034                                          const size_t proxy_user_arg_length) {
1035   DBUG_TRACE;
1036 
1037   if (proxy_user_arg_length) {
1038     m_proxy_user_length =
1039         std::min(proxy_user_arg_length, sizeof(m_proxy_user) - 1);
1040     strmake(m_proxy_user, proxy_user_arg, m_proxy_user_length);
1041   } else {
1042     *m_proxy_user = 0;
1043     m_proxy_user_length = 0;
1044   }
1045 }
1046 
1047 /**
1048   Getter method for member m_priv_host.
1049 
1050   @retval LEX_CSTRING object having constant pointer to m_priv_host.Ptr
1051           and its length
1052 */
priv_host() const1053 LEX_CSTRING Security_context::priv_host() const {
1054   LEX_CSTRING priv_host;
1055 
1056   DBUG_TRACE;
1057 
1058   priv_host.str = m_priv_host;
1059   priv_host.length = m_priv_host_length;
1060 
1061   return priv_host;
1062 }
1063 
1064 /**
1065   Setter method for member m_priv_host.
1066 
1067   @param[in]    priv_host_arg         New user value for m_priv_host.
1068   @param[in]    priv_host_arg_length  Length of "priv_host_arg" param.
1069 */
1070 
assign_priv_host(const char * priv_host_arg,const size_t priv_host_arg_length)1071 void Security_context::assign_priv_host(const char *priv_host_arg,
1072                                         const size_t priv_host_arg_length) {
1073   DBUG_TRACE;
1074 
1075   if (priv_host_arg_length) {
1076     m_priv_host_length =
1077         std::min(priv_host_arg_length, sizeof(m_priv_host) - 1);
1078     strmake(m_priv_host, priv_host_arg, m_priv_host_length);
1079   } else {
1080     *m_priv_host = 0;
1081     m_priv_host_length = 0;
1082   }
1083 }
1084 
init_restrictions(const Restrictions & restrictions)1085 void Security_context::init_restrictions(const Restrictions &restrictions) {
1086   m_restrictions = restrictions;
1087 }
1088 
is_access_restricted_on_db(ulong want_access,const std::string & db_name) const1089 bool Security_context::is_access_restricted_on_db(
1090     ulong want_access, const std::string &db_name) const {
1091   ulong filtered_access = filter_access(want_access, db_name);
1092   return (filtered_access != want_access);
1093 }
1094 
1095 /**
1096   If there is a restriction attached to an access on the given database
1097   then remove that access otherwise return the access without any change.
1098 
1099   @param[in]  access    access mask to be scanned to remove
1100   @param[in]  db_name   database to be searched in the restrictions
1101 
1102   @retval filtered access mask
1103 */
filter_access(const ulong access,const std::string & db_name) const1104 ulong Security_context::filter_access(const ulong access,
1105                                       const std::string &db_name) const {
1106   ulong access_mask = access;
1107   auto &db_restrictions = m_restrictions.db();
1108   if (db_restrictions.is_not_empty()) {
1109     ulong restrictions_mask;
1110     if (db_restrictions.find(db_name, restrictions_mask))
1111       access_mask = (access_mask & restrictions_mask) ^ access;
1112   }
1113   return access_mask;
1114 }
1115 
1116 /**
1117   Checks if the acl_user does have the asked dynamic privilege.
1118   This method assumes acl_cache_lock is already taken and ACL_USER is valid
1119 
1120   @param [in] acl_user    ACL_USER to check for privilege
1121   @param [in] privilege   privilege to check for
1122   @param [in] cumulative  Flag to decide how to fetch the privileges of ACL_USER
1123                           false - privilege granted directly or set through
1124                                   a role
1125                           true  - privileges granted directly or coming through
1126                                   roles granted to it irrespective the roles are
1127                                   active or not.
1128   @returns  pair/<has_privilege, has_with_grant_option/>
1129     @retval /<true, true/>   has required privilege with grant option
1130     @retval /<true, false/>  has required privilege without grant option
1131     @retval /<false, false/> does not have the required privilege
1132 */
fetch_global_grant(const ACL_USER & acl_user,const std::string & privilege,bool cumulative)1133 std::pair<bool, bool> Security_context::fetch_global_grant(
1134     const ACL_USER &acl_user, const std::string &privilege,
1135     bool cumulative /*= false */) {
1136   DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
1137   std::pair<bool, bool> has_privilege{false, false};
1138   Security_context sctx;
1139 
1140   const char *user = acl_user.user;
1141   const char *host = acl_user.host.get_host();
1142   // Anonymous user
1143   if (user == nullptr) user = "";
1144   if (host == nullptr) host = "";
1145 
1146   sctx.assign_priv_user(user, strlen(user));
1147   sctx.assign_priv_host(acl_user.host.get_host(), acl_user.host.get_host_len());
1148   if (cumulative) {
1149     activate_all_granted_roles(&acl_user, &sctx);
1150     sctx.checkout_access_maps();
1151   }
1152   /* Check if AuthID being processed has dynamic privilege */
1153   has_privilege = sctx.has_global_grant(privilege.c_str(), privilege.length());
1154   return has_privilege;
1155 }
1156 
1157 /**
1158   Check if required access to given table is granted.
1159 
1160   @param [in]     priv Required access
1161   @param [in,out] tables Table list object
1162 
1163   @returns access information
1164   @retval true Sucess
1165   @retval false Failure
1166  */
has_table_access(ulong priv,TABLE_LIST * tables)1167 bool Security_context::has_table_access(ulong priv, TABLE_LIST *tables) {
1168   DBUG_TRACE;
1169   DBUG_ASSERT(tables != nullptr);
1170   TABLE const *table = tables->table;
1171   LEX_CSTRING db, table_name;
1172   db.str = table->s->db.str;
1173   db.length = table->s->db.length;
1174 
1175   table_name.str = table->alias;
1176   table_name.length = strlen(table->alias);
1177 
1178   ulong acls = master_access({db.str, db.length});
1179   if (m_acl_map) {
1180     if (priv & acls) return true;
1181 
1182     acls = db_acl(db);
1183     if (priv & acls) return true;
1184 
1185     Grant_table_aggregate aggr = table_and_column_acls(db, table_name);
1186     acls = aggr.table_access | aggr.cols;
1187     if (priv & acls) return true;
1188   } else {
1189     /* Global and DB priv check */
1190     if (::check_access(m_thd, priv, db.str, &acls, nullptr, false, true))
1191       return false;
1192 
1193     if (priv & acls) return true;
1194 
1195     if (::check_grant(m_thd, priv, tables, false, 1, true)) return false;
1196     return true;
1197   }
1198   return false;
1199 }
1200 
1201 /**
1202   Check if required access to given table is not restricted.
1203 
1204   @param [in]     priv Required access
1205   @param [in,out] table Table object
1206 
1207   @returns access information
1208   @retval true Access to the table is blocked
1209   @retval false Access to the table is not blocked
1210  */
is_table_blocked(ulong priv,TABLE const * table)1211 bool Security_context::is_table_blocked(ulong priv, TABLE const *table) {
1212   DBUG_TRACE;
1213   DBUG_ASSERT(table != nullptr);
1214   LEX_CSTRING db, table_name;
1215   db.str = table->s->db.str;
1216   db.length = table->s->db.length;
1217 
1218   table_name.str = table->alias;
1219   table_name.length = strlen(table->alias);
1220 
1221   /* Table privs */
1222   TABLE_LIST tables;
1223   tables.table = const_cast<TABLE *>(table);
1224   tables.db = db.str;
1225   tables.db_length = db.length;
1226   tables.table_name = table_name.str;
1227   tables.table_name_length = table_name.length;
1228   tables.grant.privilege = NO_ACCESS;
1229 
1230   return !has_table_access(priv, &tables);
1231 }
1232 
1233 /**
1234   Check if required access to given table column is granted.
1235 
1236   @param [in] priv Required access
1237   @param [in] table Table object
1238   @param [in] columns List of column names to check
1239 
1240   @returns access information
1241   @retval true Sucess
1242   @retval false Failure
1243  */
has_column_access(ulong priv,TABLE const * table,std::vector<std::string> columns)1244 bool Security_context::has_column_access(ulong priv, TABLE const *table,
1245                                          std::vector<std::string> columns) {
1246   DBUG_TRACE;
1247   DBUG_ASSERT(table != nullptr);
1248   LEX_CSTRING db, table_name;
1249   db.str = table->s->db.str;
1250   db.length = table->s->db.length;
1251 
1252   table_name.str = table->alias;
1253   table_name.length = strlen(table->alias);
1254 
1255   /* Table privs */
1256   TABLE_LIST tables;
1257   tables.table = const_cast<TABLE *>(table);
1258   tables.db = db.str;
1259   tables.db_length = db.length;
1260   tables.table_name = table_name.str;
1261   tables.table_name_length = table_name.length;
1262   tables.grant.privilege = NO_ACCESS;
1263 
1264   // Check that general table access is possible
1265   if (!has_table_access(priv, &tables)) return false;
1266 
1267   // Try to get info about table specific grants
1268   {
1269     Acl_cache_lock_guard acl_cache_lock{this->m_thd,
1270                                         Acl_cache_lock_mode::READ_MODE};
1271     if (!acl_cache_lock.lock()) return false;
1272 
1273     if (table_hash_search(this->host().str, this->ip().str, db.str,
1274                           this->priv_user().str, table_name.str,
1275                           false) == nullptr) {
1276       // If there is no specific info about the table specific privileges, it
1277       // means that there are no column privileges configured for the table
1278       // columns. So, we let the general table access above to prevail.
1279       return true;
1280     }
1281   }
1282 
1283   for (auto column : columns) {
1284     if (check_column_grant_in_table_ref(m_thd, &tables, column.data(),
1285                                         column.length(), priv))
1286       return false;
1287   }
1288   return true;
1289 }
1290