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