Added support for 'Auth Switch Request' for 'COM_CHANGE_USER' #3504

pull/3505/head
Javier Jaramago Fernández 5 years ago
parent 9f9734df0e
commit 5e0dc81e5e

@ -238,6 +238,16 @@ class MySQL_Session
bool session_fast_forward;
bool started_sending_data_to_client; // this status variable tracks if some result set was sent to the client, or if proxysql is still buffering everything
bool use_ssl;
/**
* @brief This status variable tracks whether the session is performing an
* 'Auth Switch' due to a 'COM_CHANGE_USER' packet.
* @details It becomes 'true' when the packet is detected and processed by:
* - 'MySQL_Protocol::process_pkt_COM_CHANGE_USER'
* It's reset before sending the final response for 'Auth Switch' to the client by:
* - 'MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE'
* This flag was introduced for issue #3504.
*/
bool change_user_auth_switch;
bool with_gtid;

@ -1259,6 +1259,11 @@ bool MySQL_Protocol::generate_pkt_auth_switch_request(bool send, void **ptr, uns
myhdr.pkt_id++;
}
// Check if a 'COM_CHANGE_USER' Auth Switch is being performed in session
if ((*myds)->sess->change_user_auth_switch) {
myhdr.pkt_id=1;
}
switch((*myds)->switching_auth_type) {
case 1:
myhdr.pkt_length=1 // fe
@ -1601,6 +1606,23 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in
reply[SHA_DIGEST_LENGTH]='\0';
cur+=pass_len;
db=(char *)pkt+cur;
// Move to field after 'database'
cur += strlen(db) + 1;
// Skipt field 'character-set' (size 2)
cur += 2;
// Check and get 'Client Auth Plugin' if capability is supported
char* client_auth_plugin = nullptr;
if (pkt + len > pkt + cur) {
int capabilities = (*myds)->sess->client_myds->myconn->options.client_flag;
if (capabilities & CLIENT_PLUGIN_AUTH) {
client_auth_plugin = reinterpret_cast<char*>(pkt + cur);
}
}
// Default to 'mysql_native_password' in case 'auth_plugin' is not found.
if (client_auth_plugin == nullptr) {
client_auth_plugin = const_cast<char*>("mysql_native_password");
}
void *sha1_pass=NULL;
enum proxysql_session_type session_type = (*myds)->sess->session_type;
if (session_type == PROXYSQL_SESSION_CLICKHOUSE) {
@ -1619,10 +1641,31 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in
if (pass_len==0 && strlen(password)==0) {
ret=true;
} else {
// If pass not sent within 'COM_CHANGE_USER' packet, an 'Auth Switch Request'
// is required. We default to 'mysql_native_password'. See #3504 for more context.
if (pass_len == 0) {
// mysql_native_password
(*myds)->switching_auth_type = 1;
// started 'Auth Switch Request' for 'CHANGE_USER' in MySQL_Session.
(*myds)->sess->change_user_auth_switch = true;
generate_pkt_auth_switch_request(true, NULL, NULL);
(*myds)->myconn->userinfo->set((char *)user, NULL, db, NULL);
ret = false;
}
// If pass is sent with 'COM_CHANGE_USER', we proceed trying to use
// it to authenticate the user. See #3504 for more context.
if (password[0]!='*') { // clear text password
proxy_scramble(reply, (*myds)->myconn->scramble_buff, password);
if (memcmp(reply, pass, SHA_DIGEST_LENGTH)==0) {
ret=true;
if (strcmp(client_auth_plugin, "mysql_native_password") == 0) { // mysql_native_password
proxy_scramble(reply, (*myds)->myconn->scramble_buff, password);
if (memcmp(reply, pass, SHA_DIGEST_LENGTH)==0) {
ret=true;
}
} else { // mysql_clear_password
if (strncmp(password,(char *)pass,strlen(password))==0) {
ret=true;
}
}
} else {
if (session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_SQLITE) {

@ -492,6 +492,7 @@ MySQL_Session::MySQL_Session() {
with_gtid = false;
use_ssl = false;
change_user_auth_switch = false;
//gtid_trxid = 0;
gtid_hid = -1;
@ -985,6 +986,7 @@ void MySQL_Session::generate_proxysql_internal_session_json(json &j) {
}
}
j["client"]["DSS"] = client_myds->DSS;
j["client"]["switching_auth_type"] = client_myds->switching_auth_type;
j["default_schema"] = ( default_schema ? default_schema : "" );
j["user_attributes"] = ( user_attributes ? user_attributes : "" );
j["transaction_persistent"] = transaction_persistent;
@ -4963,6 +4965,15 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(
uint8_t _pid = 2;
if (client_myds->switching_auth_stage) _pid+=2;
if (is_encrypted) _pid++;
// If this condition is met, it means that the
// 'STATE_SERVER_HANDSHAKE' being performed isn't from the start of a
// connection, but as a consequence of a 'COM_USER_CHANGE' which
// requires an 'Auth Switch'. Thus, we impose a 'pid' of '3' for the
// response 'OK' packet. See #3504 for more context.
if (change_user_auth_switch) {
_pid = 3;
change_user_auth_switch = 0;
}
if (use_ssl == true && is_encrypted == false) {
*wrong_pass=true;
GloMyLogger->log_audit_entry(PROXYSQL_MYSQL_AUTH_ERR, this, NULL);
@ -6150,6 +6161,15 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C
// FIXME: max_connections is not handled for CHANGE_USER
} else {
l_free(pkt->size,pkt->ptr);
// 'COM_CHANGE_USER' didn't supply a password, and an 'Auth Switch Response' is
// required, going back to 'STATE_SERVER_HANDSHAKE' to perform the regular
// 'Auth Switch Response' for a connection is required. See #3504 for more context.
if (change_user_auth_switch) {
client_myds->DSS = STATE_SERVER_HANDSHAKE;
status = CONNECTING_CLIENT;
return;
}
proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Wrong credentials for frontend: disconnecting\n");
*wrong_pass=true;
// FIXME: this should become close connection

Loading…
Cancel
Save