1 /*
2  * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License, version 2.0,
6  * as published by the Free Software Foundation.
7  *
8  * This program is also distributed with certain software (including
9  * but not limited to OpenSSL) that is licensed under separate terms,
10  * as designated in a particular file or component or in included license
11  * documentation.  The authors of MySQL hereby grant you an additional
12  * permission to link the program and your derivative works with the
13  * separately licensed software that they have included with MySQL.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License, version 2.0, for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301  USA
24  */
25 
26 #include "xpl_dispatcher.h"
27 #include "xpl_session.h"
28 #include "xpl_log.h"
29 #include "xpl_server.h"
30 
31 #include "crud_cmd_handler.h"
32 #include "sql_data_context.h"
33 #include "notices.h"
34 
35 #include "ngs/scheduler.h"
36 #include "ngs/interface/client_interface.h"
37 #include "ngs/ngs_error.h"
38 #include "ngs_common/protocol_protobuf.h"
39 
40 #include <iostream>
41 
42 
Session(ngs::Client_interface & client,ngs::Protocol_encoder * proto,const Session_id session_id)43 xpl::Session::Session(ngs::Client_interface &client, ngs::Protocol_encoder *proto, const Session_id session_id)
44 : ngs::Session(client, proto, session_id),
45   m_sql(proto),
46   m_was_authenticated(false)
47 {
48 }
49 
50 
~Session()51 xpl::Session::~Session()
52 {
53   if (m_was_authenticated)
54     --Global_status_variables::instance().m_sessions_count;
55 
56   m_sql.deinit();
57 }
58 
59 
60 // handle a message while in Ready state
handle_ready_message(ngs::Request & command)61 bool xpl::Session::handle_ready_message(ngs::Request &command)
62 {
63   // check if the session got killed
64   if (m_sql.is_killed())
65   {
66     m_encoder->send_result(ngs::Error_code(ER_QUERY_INTERRUPTED, "Query execution was interrupted", "70100", ngs::Error_code::FATAL));
67     // close as fatal_error instead of killed. killed is for when the client is idle
68     on_close();
69     return true;
70   }
71 
72   if (ngs::Session::handle_ready_message(command))
73     return true;
74 
75   try
76   {
77     return dispatcher::dispatch_command(*this, m_crud_handler, m_expect_stack, command);
78   }
79   catch (ngs::Error_code &err)
80   {
81     m_encoder->send_result(err);
82     on_close();
83     return true;
84   }
85   catch (std::exception &exc)
86   {
87     // not supposed to happen, but catch exceptions as a last defense..
88     log_error("%s: Unexpected exception dispatching command: %s\n", m_client.client_id(), exc.what());
89     on_close();
90     return true;
91   }
92   return false;
93 }
94 
95 
init()96 ngs::Error_code xpl::Session::init()
97 {
98   const unsigned short port = m_client.client_port();
99   const ngs::Connection_type type = m_client.connection().connection_type();
100 
101   return m_sql.init(port, type);
102 }
103 
104 
on_kill()105 void xpl::Session::on_kill()
106 {
107   if (!m_sql.is_killed())
108   {
109     if (!m_sql.kill())
110       log_info("%s: Could not interrupt client session", m_client.client_id());
111   }
112 
113   on_close(true);
114 }
115 
116 
on_auth_success(const ngs::Authentication_handler::Response & response)117 void xpl::Session::on_auth_success(const ngs::Authentication_handler::Response &response)
118 {
119   xpl::notices::send_client_id(proto(), m_client.client_id_num());
120   ngs::Session::on_auth_success(response);
121 
122   ++Global_status_variables::instance().m_accepted_sessions_count;
123   ++Global_status_variables::instance().m_sessions_count;
124 
125   m_was_authenticated = true;
126 }
127 
128 
on_auth_failure(const ngs::Authentication_handler::Response & response)129 void xpl::Session::on_auth_failure(const ngs::Authentication_handler::Response &response)
130 {
131   if (response.error_code == ER_MUST_CHANGE_PASSWORD && !m_sql.password_expired())
132   {
133     ngs::Authentication_handler::Response r = {"Password for " MYSQLXSYS_ACCOUNT " account has been expired", response.status, response.error_code};
134     ngs::Session::on_auth_failure(r);
135   }
136   else
137     ngs::Session::on_auth_failure(response);
138 
139   ++Global_status_variables::instance().m_rejected_sessions_count;
140 }
141 
142 
mark_as_tls_session()143 void xpl::Session::mark_as_tls_session()
144 {
145   data_context().set_connection_type(ngs::Connection_tls);
146 }
147 
148 
is_handled_by(const void * handler) const149 bool xpl::Session::is_handled_by(const void *handler) const
150 {
151   return m_sql.get_thd() == handler;
152 }
153 
154 
155 /** Checks whether things owned by the given user are visible to this session.
156  Returns true if we're SUPER or the same user as the given one.
157  If user is NULL, then it's only visible for SUPER users.
158  */
can_see_user(const std::string & user) const159 bool xpl::Session::can_see_user(const std::string &user) const
160 {
161   const std::string owner = m_sql.get_authenticated_user_name();
162 
163   if (is_ready() && !owner.empty())
164   {
165     if (m_sql.has_authenticated_user_a_super_priv()
166         || (owner == user))
167       return true;
168   }
169   return false;
170 }
171 
172 
update_status(Common_status_variables::Variable Common_status_variables::* variable)173 void xpl::Session::update_status(Common_status_variables::Variable
174                                  Common_status_variables::*variable)
175 {
176   ++(m_status_variables.*variable);
177   ++(Global_status_variables::instance().*variable);
178 }
179