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 
27 #ifndef _AUTH_PLAIN_H_
28 #define _AUTH_PLAIN_H_
29 
30 
31 #include "ngs/protocol_encoder.h"
32 #include "ngs/protocol_authentication.h"
33 #include "xpl_session.h"
34 #include "xpl_server.h"
35 #include "sql_data_context.h"
36 #include "xpl_client.h"
37 
38 #include "xpl_log.h"
39 
40 
41 namespace xpl
42 {
43 
44 
45 class Sasl_plain_auth : public ngs::Authentication_handler
46 {
47 public:
create(ngs::Session_interface * session)48   static ngs::Authentication_handler_ptr create(ngs::Session_interface *session)
49   {
50     return Authentication_handler::wrap_ptr(new Sasl_plain_auth((xpl::Session*)session));
51   }
52 
handle_start(const std::string & mechanism,const std::string & data,const std::string & initial_response)53   virtual Response handle_start(const std::string &mechanism,
54                                 const std::string &data,
55                                 const std::string &initial_response)
56   {
57     Response        r;
58     const char*     client_address = m_session->client().client_address();
59     std::string     client_hostname = m_session->client().client_hostname();
60     ngs::Error_code error = sasl_message(client_hostname.empty() ? NULL : client_hostname.c_str(), client_address, data);
61 
62     // data is the username and initial_response is password
63     if (!error)
64     {
65       r.status = Succeeded;
66       r.data = "";
67       r.error_code = 0;
68     }
69     else
70     {
71       r.status = Failed;
72       r.data = error.message;
73       r.error_code = error.error;
74     }
75 
76     return r;
77   }
78 
handle_continue(const std::string & data)79   virtual Response handle_continue(const std::string &data)
80   {
81     // never supposed to get called
82     Response r;
83     r.status = Error;
84     r.error_code = ER_NET_PACKETS_OUT_OF_ORDER;
85     return r;
86   }
87 
done()88   virtual void done()
89   {
90     delete this;
91   }
92 
93 protected:
Sasl_plain_auth(xpl::Session * session)94   Sasl_plain_auth(xpl::Session *session) : m_session(session) {}
95 
96 private:
97   xpl::Session *m_session;
98 
sasl_message(const char * client_hostname,const char * client_address,const std::string & message)99   ngs::Error_code sasl_message(const char *client_hostname, const char *client_address, const std::string &message)
100   {
101     try
102     {
103       const std::size_t  sasl_element_max_with_two_additional_bytes = 256;
104       std::size_t        message_position = 0;
105 
106       char authzid_db[sasl_element_max_with_two_additional_bytes];
107       char authcid[sasl_element_max_with_two_additional_bytes];
108       char passwd[sasl_element_max_with_two_additional_bytes];
109 
110       if (!extract_null_terminated_element(message, message_position, sasl_element_max_with_two_additional_bytes, authzid_db) ||
111           !extract_null_terminated_element(message, message_position, sasl_element_max_with_two_additional_bytes, authcid) ||
112           !extract_null_terminated_element(message, message_position, sasl_element_max_with_two_additional_bytes, passwd))
113       {
114 //        throw ngs::Error_code(ER_INVALID_CHARACTER_STRING, "Invalid format of login string");
115         throw ngs::Error_code(ER_NO_SUCH_USER, "Invalid user or password");
116       }
117 
118       if (strlen(authcid) == 0)
119         throw ngs::Error_code(ER_NO_SUCH_USER, "Invalid user or password");
120       std::string password_hash = *passwd ? compute_password_hash(passwd) : "";
121       On_user_password_hash      check_password_hash = ngs::bind(&Sasl_plain_auth::compare_hashes, this, password_hash, ngs::placeholders::_1);
122       ngs::IOptions_session_ptr  options_session = m_session->client().connection().options();
123       const ngs::Connection_type connection_type = m_session->client().connection().connection_type();
124 
125       return m_session->data_context().authenticate(authcid, client_hostname, client_address, authzid_db, check_password_hash,
126                                                     ((xpl::Client&)m_session->client()).supports_expired_passwords(), options_session, connection_type);
127     }
128     catch(const ngs::Error_code &error_code)
129     {
130       return error_code;
131     }
132     return ngs::Error_code();
133   }
134 
compare_hashes(const std::string & user_password_hash,const std::string & db_password_hash)135   bool compare_hashes(const std::string &user_password_hash, const std::string &db_password_hash)
136   {
137     const bool result = (user_password_hash == db_password_hash);
138     return result;
139   }
140 };
141 
142 }  // namespace xpl
143 
144 
145 #endif // _AUTH_PLAIN_H_
146