Server IP : 104.21.38.3 / Your IP : 172.70.188.61 Web Server : Apache System : Linux krdc-ubuntu-s-2vcpu-4gb-amd-blr1-01.localdomain 5.15.0-142-generic #152-Ubuntu SMP Mon May 19 10:54:31 UTC 2025 x86_64 User : www ( 1000) PHP Version : 7.4.33 Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /www/server/mysql/src/rapid/plugin/x/src/ |
Upload File : |
/* * Copyright (c) 2015, 2023, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is also distributed with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, * as designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have included with MySQL. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include <algorithm> #include "mysql/plugin.h" #include "sql_data_context.h" #include "sql_user_require.h" #include "mysql/service_command.h" #include "streaming_command_delegate.h" #include "buffering_command_delegate.h" #include "mysql_variables.h" #include "query_string_builder.h" #include "xpl_log.h" #include "xpl_error.h" #include "notices.h" #include "user_verification_helper.h" using namespace xpl; ngs::Error_code Sql_data_context::init(const int client_port, const ngs::Connection_type type) { ngs::Error_code error = init(); if (error) return error; if ((error = set_connection_type(type))) return error; if (0 != srv_session_info_set_client_port(m_mysql_session, client_port)) return ngs::Error_code(ER_X_SESSION, "Could not set session client port"); return ngs::Error_code(); } ngs::Error_code Sql_data_context::init() { m_mysql_session = srv_session_open(&Sql_data_context::default_completion_handler, this); log_debug("sqlsession init: %p [%i]", m_mysql_session, m_mysql_session ? srv_session_info_get_session_id(m_mysql_session) : -1); if (!m_mysql_session) { if (ER_SERVER_ISNT_AVAILABLE == m_last_sql_errno) return ngs::Error_code(ER_SERVER_ISNT_AVAILABLE, "Server API not ready"); log_error("Could not open internal MySQL session"); return ngs::Error_code(ER_X_SESSION, "Could not open session"); } return ngs::Error_code(); } void Sql_data_context::deinit() { if (m_mysql_session) { srv_session_detach(m_mysql_session); log_debug("sqlsession deinit: %p [%i]", m_mysql_session, srv_session_info_get_session_id(m_mysql_session)); srv_session_close(m_mysql_session); m_mysql_session = NULL; } #ifdef HAVE_PSI_THREAD_INTERFACE PSI_THREAD_CALL(delete_current_thread)(); PSI_thread *psi= PSI_THREAD_CALL(new_thread) (KEY_thread_x_worker, NULL, 0); PSI_THREAD_CALL(set_thread_os_id)(psi); PSI_THREAD_CALL(set_thread)(psi); #endif /* HAVE_PSI_THREAD_INTERFACE */ } static void kill_completion_handler(void *ctx, unsigned int sql_errno, const char *err_msg) { log_warning("Kill client: %i %s", sql_errno, err_msg); } bool Sql_data_context::kill() { if (srv_session_server_is_available()) { log_debug("sqlsession init (for kill): %p [%i]", m_mysql_session, m_mysql_session ? srv_session_info_get_session_id(m_mysql_session) : -1); MYSQL_SESSION session = srv_session_open(kill_completion_handler, NULL); bool ok = false; if (session) { MYSQL_SECURITY_CONTEXT scontext; if (thd_get_security_context(srv_session_info_get_thd(session), &scontext)) log_warning("Could not get security context for session"); else { const char *user = MYSQL_SESSION_USER; const char *host = MYSQLXSYS_HOST; if (security_context_lookup(scontext, user, host, NULL, NULL)) log_warning("Unable to switch security context to root"); else { COM_DATA data; Callback_command_delegate deleg; Query_string_builder qb; qb.put("KILL ").put(mysql_session_id()); data.com_query.query = (char*)qb.get().c_str(); data.com_query.length = static_cast<unsigned int>(qb.get().length()); if (!command_service_run_command(session, COM_QUERY, &data, mysqld::get_charset_utf8mb4_general_ci(), deleg.callbacks(), deleg.representation(), &deleg)) { if (!deleg.get_error()) ok = true; else log_info("Kill client: %i %s", deleg.get_error().error, deleg.get_error().message.c_str()); } } } srv_session_close(session); } return ok; } return false; } ngs::Error_code Sql_data_context::set_connection_type(const ngs::Connection_type type) { enum_vio_type vio_type = ngs::Connection_type_helper::convert_type(type); if (NO_VIO_TYPE == vio_type) return ngs::Error(ER_X_SESSION, "Connection type not known. type=%i", (int)type); if (0 != srv_session_info_set_connection_type(m_mysql_session, vio_type)) return ngs::Error_code(ER_X_SESSION, "Could not set session connection type"); return ngs::Error_code(); } bool Sql_data_context::wait_api_ready(ngs::function<bool()> exiting) { bool result = is_api_ready(); while (!result && !exiting()) { my_sleep(250000); // wait for 0.25s result = is_api_ready(); } return result; } void Sql_data_context::detach() { if (m_mysql_session) srv_session_detach(m_mysql_session); } Sql_data_context::~Sql_data_context() { if (m_mysql_session) log_debug("sqlsession deinit~: %p [%i]", m_mysql_session, srv_session_info_get_session_id(m_mysql_session)); if (m_mysql_session && srv_session_close(m_mysql_session)) log_warning("Error closing SQL session"); } void Sql_data_context::switch_to_local_user(const std::string &user) { ngs::Error_code error = switch_to_user(user.c_str(), "localhost", NULL, NULL); if (error) throw error; } ngs::Error_code Sql_data_context::authenticate(const char *user, const char *host, const char *ip, const char *db, On_user_password_hash password_hash_cb, bool allow_expired_passwords, ngs::IOptions_session_ptr &options_session, const ngs::Connection_type type) { ngs::Error_code error = switch_to_user(user, host, ip, db); if (error) { return ngs::Error(ER_NO_SUCH_USER, "Invalid user or password"); } std::string authenticated_user_name = get_authenticated_user_name(); std::string authenticated_user_host = get_authenticated_user_host(); error = switch_to_user(MYSQL_SESSION_USER, MYSQLXSYS_HOST, NULL, NULL); if (error) { log_error("Unable to switch context to user %s", MYSQL_SESSION_USER); return error; } if (!is_acl_disabled()) { User_verification_helper user_verification(password_hash_cb, options_session, type); error = user_verification.verify_mysql_account( *this, authenticated_user_name, authenticated_user_host); } if (error.error == ER_MUST_CHANGE_PASSWORD_LOGIN) { m_password_expired = true; // password is expired, client doesn't support it and server wants us to disconnect these users if (error.severity == ngs::Error_code::FATAL && !allow_expired_passwords) return error; // if client supports expired password mode, then pwd expired is not fatal // we send a notice and move on notices::send_account_expired(proto()); } else if (error) return error; error = switch_to_user(user, host, ip, db); if (!error) { if (db && *db) { COM_DATA data; data.com_init_db.db_name = db; data.com_init_db.length = static_cast<unsigned long>(strlen(db)); m_callback_delegate.reset(); if (command_service_run_command(m_mysql_session, COM_INIT_DB, &data, mysqld::get_charset_utf8mb4_general_ci(), m_callback_delegate.callbacks(), m_callback_delegate.representation(), &m_callback_delegate)) return ngs::Error_code(ER_NO_DB_ERROR, "Could not set database"); error = m_callback_delegate.get_error(); } std::string user_name = get_user_name(); std::string host_or_ip = get_host_or_ip(); #ifdef HAVE_PSI_THREAD_INTERFACE PSI_THREAD_CALL(set_thread_account) ( user_name.c_str(), user_name.length(), host_or_ip.c_str(), host_or_ip.length()); #endif // HAVE_PSI_THREAD_INTERFACE return error; } log_error("Unable to switch context to user %s", user); return error; } template<typename Result_type> bool get_security_context_value(MYSQL_THD thd, const char *option, Result_type &result) { MYSQL_SECURITY_CONTEXT scontext; if (thd_get_security_context(thd, &scontext)) return false; return FALSE == security_context_get_option(scontext, option, &result); } bool Sql_data_context::is_acl_disabled() { MYSQL_LEX_CSTRING value; if (get_security_context_value(get_thd(), "priv_user", value)) { return 0 != value.length && NULL != strstr(value.str, "skip-grants "); } return false; } bool Sql_data_context::has_authenticated_user_a_super_priv() const { my_svc_bool value = 0; if (get_security_context_value(get_thd(), "privilege_super", value)) return value != 0; return false; } std::string Sql_data_context::get_user_name() const { MYSQL_LEX_CSTRING result; if (get_security_context_value(get_thd(), "user", result)) return result.str; return ""; } std::string Sql_data_context::get_host_or_ip() const { MYSQL_LEX_CSTRING result; if (get_security_context_value(get_thd(), "host_or_ip", result)) return result.str; return ""; } std::string Sql_data_context::get_authenticated_user_name() const { MYSQL_LEX_CSTRING result; if (get_security_context_value(get_thd(), "priv_user", result)) return result.str; return ""; } std::string Sql_data_context::get_authenticated_user_host() const { MYSQL_LEX_CSTRING result; if (get_security_context_value(get_thd(), "priv_host", result)) return result.str; return ""; } ngs::Error_code Sql_data_context::switch_to_user( const char *username, const char *hostname, const char *address, const char *db) { MYSQL_SECURITY_CONTEXT scontext; m_auth_ok = false; if (thd_get_security_context(get_thd(), &scontext)) return ngs::Fatal(ER_X_SERVICE_ERROR, "Error getting security context for session"); // security_context_lookup - doesn't make a copy of username, hostname, addres or db // thus we need to make a copy of them and pass our pointers // to security_context_lookup m_username = username ? username : ""; m_hostname = hostname ? hostname : ""; m_address = address ? address : ""; m_db = db ? db : ""; log_debug("Switching security context to user %s@%s [%s]", username, hostname, address); if (security_context_lookup(scontext, m_username.c_str(), m_hostname.c_str(), m_address.c_str(), m_db.c_str())) { return ngs::Fatal(ER_X_SERVICE_ERROR, "Unable to switch context to user %s", username); } m_auth_ok = true; return ngs::Success(); } ngs::Error_code Sql_data_context::execute_kill_sql_session(uint64_t mysql_session_id) { Query_string_builder qb; qb.put("KILL ").put(mysql_session_id); Sql_data_context::Result_info r_info; return execute_sql_no_result(qb.get().data(), qb.get().length(), r_info); } ngs::Error_code Sql_data_context::execute_sql( Command_delegate &deleg, const char *sql, size_t length, Sql_data_context::Result_info &r_info) { if (!m_auth_ok && !m_query_without_authentication) throw std::logic_error("Attempt to execute query in non-authenticated session"); COM_DATA data; data.com_query.query = sql; data.com_query.length = static_cast<unsigned int>(length); deleg.reset(); if (command_service_run_command(m_mysql_session, COM_QUERY, &data, mysqld::get_charset_utf8mb4_general_ci(), deleg.callbacks(), deleg.representation(), &deleg)) { log_debug("Error running command: %s (%i %s)", sql, m_last_sql_errno, m_last_sql_error.c_str()); return ngs::Error_code(ER_X_SERVICE_ERROR, "Internal error executing query"); } if (m_password_expired && !deleg.get_error()) { // if a SQL command succeeded while password is expired, it means the user probably changed the password // we run a command to check just in case... (some commands are still allowed in expired password mode) Callback_command_delegate d; data.com_query.query = "select 1"; data.com_query.length = static_cast<unsigned int>(strlen(data.com_query.query)); if (false == command_service_run_command(m_mysql_session, COM_QUERY, &data, mysqld::get_charset_utf8mb4_general_ci(), d.callbacks(), d.representation(), &d) && !d.get_error()) { m_password_expired = false; } } if (is_killed()) throw ngs::Fatal(ER_QUERY_INTERRUPTED, "Query execution was interrupted"); r_info.last_insert_id = deleg.last_insert_id(); r_info.num_warnings = deleg.statement_warn_count(); r_info.affected_rows = deleg.affected_rows(); r_info.message = deleg.message(); r_info.server_status = deleg.server_status(); return deleg.get_error(); } ngs::Error_code Sql_data_context::execute_sql_no_result(const char *sql, std::size_t sql_len, Sql_data_context::Result_info &r_info) { m_callback_delegate.set_callbacks(Callback_command_delegate::Start_row_callback(), Callback_command_delegate::End_row_callback()); return execute_sql(m_callback_delegate, sql, sql_len, r_info); } ngs::Error_code Sql_data_context::execute_sql_and_collect_results(const char *sql, std::size_t sql_len, std::vector<Command_delegate::Field_type> &r_types, Buffering_command_delegate::Resultset &r_rows, Result_info &r_info) { ngs::Error_code error = execute_sql(m_buffering_delegate, sql, sql_len, r_info); if (!error) { r_types = m_buffering_delegate.get_field_types(); r_rows = m_buffering_delegate.resultset(); } return error; } ngs::Error_code Sql_data_context::execute_sql_and_process_results(const char *sql, std::size_t sql_len, const Callback_command_delegate::Start_row_callback &start_row, const Callback_command_delegate::End_row_callback &end_row, Sql_data_context::Result_info &r_info) { m_callback_delegate.set_callbacks(start_row, end_row); return execute_sql(m_callback_delegate, sql, sql_len, r_info); } ngs::Error_code Sql_data_context::execute_sql_and_stream_results(const char *sql, std::size_t sql_len, bool compact_metadata, Result_info &r_info) { m_streaming_delegate.set_compact_metadata(compact_metadata); return execute_sql(m_streaming_delegate, sql, sql_len, r_info); } void Sql_data_context::default_completion_handler(void *ctx, unsigned int sql_errno, const char *err_msg) { Sql_data_context *self = (Sql_data_context*)ctx; self->m_last_sql_errno = sql_errno; self->m_last_sql_error = err_msg ? err_msg : ""; } bool Sql_data_context::is_killed() { return srv_session_info_killed(m_mysql_session); } bool Sql_data_context::is_api_ready() { return 0 != srv_session_server_is_available(); } uint64_t Sql_data_context::mysql_session_id() const { return srv_session_info_get_session_id(m_mysql_session); } MYSQL_THD Sql_data_context::get_thd() const { return srv_session_info_get_thd(m_mysql_session); }