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 #include "auth_mysql41.h"
28 #include "sql_data_context.h"
29
30 #include "mysql_com.h"
31 #include "crypt_genhash_impl.h"
32 #include "password.h"
33
34 // C -> S: authenticationStart(MYSQL41)
35 // S -> C: authenticationContinue(20 byte salt/scramble)
36 // C -> S: authenticationContinue(schema\0user\0sha1(sha1(password))+salt)
37 // S -> C: Notice(password expired etc)
38 // S -> C: authenticationOk/Error
39
40 using namespace xpl;
41
42
handle_start(const std::string & mechanism,const std::string & data,const std::string & initial_response)43 ngs::Authentication_handler::Response Sasl_mysql41_auth::handle_start(const std::string &mechanism,
44 const std::string &data,
45 const std::string &initial_response)
46 {
47 Response r;
48
49 if (m_state == S_starting)
50 {
51 m_salt.resize(SCRAMBLE_LENGTH);
52 ::generate_user_salt(&m_salt[0], static_cast<int>(m_salt.size()));
53 r.data = m_salt;
54 r.status = Ongoing;
55 r.error_code = 0;
56
57 m_state = S_waiting_response;
58 }
59 else
60 {
61 r.status = Error;
62 r.error_code = ER_NET_PACKETS_OUT_OF_ORDER;
63
64 m_state = S_error;
65 }
66
67 return r;
68 }
69
handle_continue(const std::string & data)70 ngs::Authentication_handler::Response Sasl_mysql41_auth::handle_continue(const std::string &data)
71 {
72 Response r;
73
74 if (m_state == S_waiting_response)
75 {
76 const char* client_address = m_session->client().client_address();
77 std::string client_hostname = m_session->client().client_hostname();
78 ngs::Error_code error = sasl_message(client_hostname.empty() ? NULL : client_hostname.c_str(), client_address, data);
79
80 // data is the username and initial_response is password
81 if (!error)
82 {
83 r.status = Succeeded;
84 r.error_code = 0;
85 }
86 else
87 {
88 r.status = Failed;
89 r.data = error.message;
90 r.error_code = error.error;
91 }
92 m_state = S_done;
93 }
94 else
95 {
96 m_state = S_error;
97 r.status = Error;
98 r.error_code = ER_NET_PACKETS_OUT_OF_ORDER;
99 }
100 return r;
101 }
102
103
sasl_message(const char * client_hostname,const char * client_address,const std::string & message)104 ngs::Error_code Sasl_mysql41_auth::sasl_message(const char *client_hostname, const char *client_address, const std::string &message)
105 {
106 try
107 {
108 const std::size_t sasl_element_max_with_two_additional_bytes = 256;
109 std::size_t message_position = 0;
110
111 char authzid[sasl_element_max_with_two_additional_bytes];
112 char authcid[sasl_element_max_with_two_additional_bytes];
113 char passwd[sasl_element_max_with_two_additional_bytes];
114
115 if (!extract_null_terminated_element(message, message_position, sasl_element_max_with_two_additional_bytes, authzid) ||
116 !extract_null_terminated_element(message, message_position, sasl_element_max_with_two_additional_bytes, authcid) ||
117 !extract_null_terminated_element(message, message_position, sasl_element_max_with_two_additional_bytes, passwd))
118 {
119 //throw ngs::Error_code(ER_INVALID_CHARACTER_STRING, "Invalid format of login string");
120 throw ngs::Error_code(ER_NO_SUCH_USER, "Invalid user or password");
121 }
122
123 if (strlen(authcid) == 0)
124 throw ngs::Error_code(ER_NO_SUCH_USER, "Invalid user or password");
125
126 On_user_password_hash verify_password_hash = ngs::bind(&Sasl_mysql41_auth::check_password_hash, this, passwd, ngs::placeholders::_1);
127 ngs::IOptions_session_ptr options_session = m_session->client().connection().options();
128 const ngs::Connection_type connection_type = m_session->client().connection().connection_type();
129
130 return m_session->data_context().authenticate(authcid, client_hostname, client_address, authzid, verify_password_hash,
131 ((xpl::Client&)m_session->client()).supports_expired_passwords(), options_session, connection_type);
132 }
133 catch(const ngs::Error_code &error_code)
134 {
135 return error_code;
136 }
137 return ngs::Error_code();
138 }
139
140 #include "mysql_com.h"
141
check_password_hash(const std::string & password_scramble,const std::string & password_hash)142 bool Sasl_mysql41_auth::check_password_hash(const std::string &password_scramble, const std::string &password_hash)
143 {
144 try
145 {
146 if (password_scramble.empty())
147 {
148 // client gave no password, this can only login to a no password acct
149 if (password_hash.empty())
150 return true;
151 return false;
152 }
153 if (!password_hash.empty())
154 {
155 uint8 db_hash_stage2[SCRAMBLE_LENGTH+1] = {0};
156 uint8 user_hash_stage2[SCRAMBLE_LENGTH+1] = {0};
157
158 ::get_salt_from_password(db_hash_stage2, password_hash.c_str());
159 ::get_salt_from_password(user_hash_stage2, password_scramble.c_str());
160
161 return 0 == ::check_scramble((const uchar*)user_hash_stage2, m_salt.c_str(), db_hash_stage2);
162 }
163 return false;
164 }
165 catch(const ngs::Error_code&)
166 {
167 return false;
168 }
169 }
170