diff --git a/.gitignore b/.gitignore index e1dd6de8e2..0c6306fb85 100644 --- a/.gitignore +++ b/.gitignore @@ -85,6 +85,7 @@ deps/libinjection/libinjection-*/ deps/libmicrohttpd/libmicrohttpd-*/ deps/libmicrohttpd/libmicrohttpd deps/libssl/openssl-openssl-*/ +deps/libssl/openssl-*/ deps/lz4/lz4-*/ deps/mariadb-client-library/mariadb-connector-c-*/ deps/pcre/pcre-*/ diff --git a/include/MySQL_Data_Stream.h b/include/MySQL_Data_Stream.h index 7f24206605..77921a400e 100644 --- a/include/MySQL_Data_Stream.h +++ b/include/MySQL_Data_Stream.h @@ -157,6 +157,7 @@ class MySQL_Data_Stream int switching_auth_stage; int switching_auth_type; + int auth_in_progress; // if 0 , no authentication is in progress. Any value greater than 0 depends from the implementation unsigned int tmp_charset; short revents; diff --git a/include/MySQL_Protocol.h b/include/MySQL_Protocol.h index a7b68994c3..662912bf1d 100644 --- a/include/MySQL_Protocol.h +++ b/include/MySQL_Protocol.h @@ -91,6 +91,36 @@ uint8_t mysql_decode_length(unsigned char *ptr, uint64_t *len); */ my_bool proxy_mysql_stmt_close(MYSQL_STMT* mysql_stmt); +class MyProt_tmp_auth_vars { + public: + unsigned char *user = NULL; + char *db = NULL; + char *db_tmp = NULL; + unsigned char *pass = NULL; + char *password = NULL; + unsigned char *auth_plugin = NULL; + void *sha1_pass=NULL; + unsigned char *_ptr = NULL;; + unsigned int charset; + uint32_t capabilities = 0; + uint32_t max_pkt; + uint32_t pass_len; + bool use_ssl = false; + enum proxysql_session_type session_type; +}; + +class MyProt_tmp_auth_attrs { + public: + char *default_schema = NULL; + char *attributes = NULL; + int default_hostgroup=-1; + int max_connections; + bool schema_locked; + bool transaction_persistent = true; + bool fast_forward = false; + bool _ret_use_ssl = false; +}; + class MySQL_Protocol { private: MySQL_Connection_userinfo *userinfo; @@ -101,10 +131,14 @@ class MySQL_Protocol { bool dump_pkt; #endif MySQL_Prepared_Stmt_info *current_PreStmt; + int auth_plugin_id; uint16_t prot_status; + bool more_data_needed; MySQL_Data_Stream *get_myds() { return *myds; } MySQL_Protocol() { + auth_plugin_id = 0; prot_status=0; + more_data_needed = false; } void init(MySQL_Data_Stream **, MySQL_Connection_userinfo *, MySQL_Session *); @@ -139,6 +173,20 @@ class MySQL_Protocol { // - pointer to the packet // - size of the packet bool process_pkt_handshake_response(unsigned char *pkt, unsigned int len); + + // all the following functions were inline inside process_pkt_handshake_response() , but it was split for readibility + int PPHR_1(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1); + bool PPHR_2(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1); + void PPHR_3(MyProt_tmp_auth_vars& vars1); + bool PPHR_4auth0(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1); + bool PPHR_4auth1(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1); + void PPHR_5passwordTrue(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1, char * reply, MyProt_tmp_auth_attrs& attr1); + void PPHR_5passwordFalse_0(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1, char * reply, MyProt_tmp_auth_attrs& attr1); + void PPHR_5passwordFalse_auth2(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1, char * reply, MyProt_tmp_auth_attrs& attr1 , void *& sha1_pass); + void PPHR_7auth1(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1, char * reply, MyProt_tmp_auth_attrs& attr1 , void *& sha1_pass); + void PPHR_7auth2(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1, char * reply, MyProt_tmp_auth_attrs& attr1 , void *& sha1_pass); + void PPHR_SetConnAttrs(MyProt_tmp_auth_vars& vars1, MyProt_tmp_auth_attrs& attr1); + bool process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned int len); void * Query_String_to_packet(uint8_t sid, std::string *s, unsigned int *l); /** diff --git a/lib/MySQL_Protocol.cpp b/lib/MySQL_Protocol.cpp index 756c28f970..655e3beb68 100644 --- a/lib/MySQL_Protocol.cpp +++ b/lib/MySQL_Protocol.cpp @@ -35,6 +35,12 @@ extern ClickHouse_Authentication *GloClickHouseAuth; extern const MARIADB_CHARSET_INFO * proxysql_find_charset_nr(unsigned int nr); MARIADB_CHARSET_INFO * proxysql_find_charset_name(const char *name); +static const char *plugins[3] = { + "mysql_native_password", + "mysql_clear_password", + "caching_sha2_password", +}; + #ifdef DEBUG static void __dump_pkt(const char *func, unsigned char *_ptr, unsigned int len) { @@ -1376,13 +1382,15 @@ bool MySQL_Protocol::verify_user_pass( char reply[SHA_DIGEST_LENGTH+1]; reply[SHA_DIGEST_LENGTH]='\0'; - int auth_plugin_id = 0; + auth_plugin_id = 0; - if (strncmp((char *)auth_plugin,(char *)"mysql_native_password",strlen((char *)"mysql_native_password"))==0) { + if (strncmp((char *)auth_plugin,plugins[0],strlen(plugins[0]))==0) { // mysql_native_password auth_plugin_id = 1; - } - if (strncmp((char *)auth_plugin,(char *)"mysql_clear_password",strlen((char *)"mysql_clear_password"))==0) { + } else if (strncmp((char *)auth_plugin,plugins[1],strlen(plugins[1]))==0) { // mysql_clear_password auth_plugin_id = 2; + } else if (strncmp((char *)auth_plugin,plugins[2],strlen(plugins[2]))==0) { // caching_sha2_password + //auth_plugin_id = 3; // FIXME: this is temporary, because yet not supported + auth_plugin_id = 0; // FIXME: this is temporary, because yet not supported . It must become 3 } if (password[0]!='*') { // clear text password @@ -1391,10 +1399,16 @@ bool MySQL_Protocol::verify_user_pass( if (memcmp(reply, pass, SHA_DIGEST_LENGTH)==0) { ret=true; } - } else { // mysql_clear_password + } else if (auth_plugin_id == 2) { // mysql_clear_password if (strncmp(password,(char *)pass,strlen(password))==0) { ret=true; } + } else if (auth_plugin_id == 3) { // caching_sha2_password + // FIXME: not supported yet + // we assert() here because auth_plugin_id should never be 3 unless it is fully implemented + assert(0); + } else { + ret = false; } } else { if (auth_plugin_id == 1) { @@ -1635,89 +1649,46 @@ bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned in return ret; } -bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned int len) { -#ifdef DEBUG - if (dump_pkt) { __dump_pkt(__func__,pkt,len); } -#endif - bool ret = false; - unsigned int charset; - uint32_t capabilities = 0; - uint32_t max_pkt; - uint32_t pass_len; - unsigned char *user = NULL; - char *db = NULL; - char *db_tmp = NULL; - unsigned char *pass = NULL; - MySQL_Connection *myconn = NULL; - char *password = NULL; - bool use_ssl = false; - bool _ret_use_ssl = false; - unsigned char *auth_plugin = NULL; - int auth_plugin_id = 0; - - char reply[SHA_DIGEST_LENGTH+1] = { 0 }; - int default_hostgroup=-1; - char *default_schema = NULL; - char *attributes = NULL; - bool schema_locked; - bool transaction_persistent = true; - bool fast_forward = false; - int max_connections; - enum proxysql_session_type session_type = (*myds)->sess->session_type; - - void *sha1_pass=NULL; -//#ifdef DEBUG - unsigned char *_ptr=pkt; -//#endif - mysql_hdr hdr; - memcpy(&hdr,pkt,sizeof(mysql_hdr)); - //Copy4B(&hdr,pkt); - pkt += sizeof(mysql_hdr); - - // NOTE: 'mysqlsh' sends a 'COM_INIT_DB' as soon as the connection is openned - // before ProxySQL has sent 'Server Greeting' messsage. Because this packet is - // unexpected, we simple return 'false' and exit. - if (hdr.pkt_id == 0 && *pkt == 2) { +// this function was inline in process_pkt_handshake_response() , split for readibility +int MySQL_Protocol::PPHR_1(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1) { // process_pkt_handshake_response inner 1 + (*myds)->switching_auth_stage=2; + (*myds)->auth_in_progress = 0; + if (len==5) { ret = false; - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . Client is disconnecting\n", (*myds), (*myds)->sess, user); - goto __exit_process_pkt_handshake_response; - } - - if ((*myds)->myconn->userinfo->username) { - (*myds)->switching_auth_stage=2; - if (len==5) { - ret = false; - user = (unsigned char *)(*myds)->myconn->userinfo->username; - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . Client is disconnecting\n", (*myds), (*myds)->sess, user); - proxy_error("User '%s'@'%s' is disconnecting during switch auth\n", user, (*myds)->addr.addr); - goto __exit_process_pkt_handshake_response; - } - auth_plugin_id = (*myds)->switching_auth_type; - if (auth_plugin_id==1) { - pass_len = len - sizeof(mysql_hdr); - } else { - pass_len=strlen((char *)pkt); - } - pass = (unsigned char *)malloc(pass_len+1); - memcpy(pass, pkt, pass_len); - pass[pass_len] = 0; - user = (unsigned char *)(*myds)->myconn->userinfo->username; - db = (*myds)->myconn->userinfo->schemaname; - //(*myds)->switching_auth_stage=2; - charset=(*myds)->tmp_charset; - proxy_debug(PROXY_DEBUG_MYSQL_PROTOCOL,2,"Session=%p , DS=%p . Encrypted: %d , switching_auth: %d, auth_plugin_id: %d\n", (*myds)->sess, (*myds), (*myds)->encrypted, (*myds)->switching_auth_stage, auth_plugin_id); - capabilities = (*myds)->myconn->options.client_flag; - goto __do_auth; + vars1.user = (unsigned char *)(*myds)->myconn->userinfo->username; + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . Client is disconnecting\n", (*myds), (*myds)->sess, vars1.user); + proxy_error("User '%s'@'%s' is disconnecting during switch auth\n", vars1.user, (*myds)->addr.addr); + (*myds)->auth_in_progress = 0; + return 1; } + auth_plugin_id = (*myds)->switching_auth_type; + if (auth_plugin_id==1) { + vars1.pass_len = len - sizeof(mysql_hdr); + } else { + vars1.pass_len=strlen((char *)pkt); + } + vars1.pass = (unsigned char *)malloc(vars1.pass_len+1); + memcpy(vars1.pass, pkt, vars1.pass_len); + vars1.pass[vars1.pass_len] = 0; + vars1.user = (unsigned char *)(*myds)->myconn->userinfo->username; + vars1.db = (*myds)->myconn->userinfo->schemaname; + //(*myds)->switching_auth_stage=2; + vars1.charset=(*myds)->tmp_charset; + proxy_debug(PROXY_DEBUG_MYSQL_PROTOCOL,2,"Session=%p , DS=%p . Encrypted: %d , switching_auth: %d, auth_plugin_id: %d\n", (*myds)->sess, (*myds), (*myds)->encrypted, (*myds)->switching_auth_stage, auth_plugin_id); + vars1.capabilities = (*myds)->myconn->options.client_flag; + return 2; +} - capabilities = CPY4(pkt); +// this function was inline in process_pkt_handshake_response() , split for readibility +bool MySQL_Protocol::PPHR_2(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1) { // process_pkt_handshake_response inner 2 + vars1.capabilities = CPY4(pkt); // see bug #2916. If CLIENT_MULTI_STATEMENTS is set by the client // we enforce setting CLIENT_MULTI_RESULTS, this is the proper and expected // behavior (refer to 'https://dev.mysql.com/doc/c-api/8.0/en/c-api-multiple-queries.html'). // Don't enforcing this would cause a mismatch between client and backend // connections flags. - if (capabilities & CLIENT_MULTI_STATEMENTS) { - capabilities |= CLIENT_MULTI_RESULTS; + if (vars1.capabilities & CLIENT_MULTI_STATEMENTS) { + vars1.capabilities |= CLIENT_MULTI_RESULTS; } // we enforce disabling 'CLIENT_DEPRECATE_EOF' from the supported capabilities // in case it's explicitly disabled by global variable 'mysql_thread___enable_client_deprecate_eof'. @@ -1729,25 +1700,25 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned // First step is replying to client during initial handshake (in 'generate_pkt_initial_handshake') // specifying no 'CLIENT_DEPRECATE_EOF' support in 'server_capabilities'. if (!mysql_thread___enable_client_deprecate_eof) { - capabilities &= ~CLIENT_DEPRECATE_EOF; - } - (*myds)->myconn->options.client_flag = capabilities; - pkt += sizeof(uint32_t); - max_pkt = CPY4(pkt); - (*myds)->myconn->options.max_allowed_pkt = max_pkt; - pkt += sizeof(uint32_t); - charset = *(uint8_t *)pkt; + vars1.capabilities &= ~CLIENT_DEPRECATE_EOF; + } + (*myds)->myconn->options.client_flag = vars1.capabilities; + pkt += sizeof(uint32_t); + vars1.max_pkt = CPY4(pkt); + (*myds)->myconn->options.max_allowed_pkt = vars1.max_pkt; + pkt += sizeof(uint32_t); + vars1.charset = *(uint8_t *)pkt; if ( (*myds)->encrypted == false ) { // client wants to use SSL if (len == sizeof(mysql_hdr)+32) { (*myds)->encrypted = true; - use_ssl = true; + vars1.use_ssl = true; ret = false; - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . goto __exit_process_pkt_handshake_response\n", (*myds), (*myds)->sess, user); - goto __exit_process_pkt_handshake_response; + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . goto __exit_process_pkt_handshake_response\n", (*myds), (*myds)->sess, vars1.user); + return false; } } // see bug #810 - if (charset==0) { + if (vars1.charset==0) { const MARIADB_CHARSET_INFO *ci = NULL; ci = proxysql_find_charset_name(mysql_thread___default_variables[SQL_CHARACTER_SET]); if (!ci) { @@ -1756,204 +1727,487 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned assert(0); // LCOV_EXCL_STOP } - charset=ci->nr; + vars1.charset=ci->nr; } - (*myds)->tmp_charset=charset; - pkt += 24; + (*myds)->tmp_charset = vars1.charset; + pkt += 24; // if (len==sizeof(mysql_hdr)+32) { // (*myds)->encrypted=true; // use_ssl=true; // } else { - user = pkt; - pkt += strlen((char *)user) + 1; + vars1.user = pkt; + pkt += strlen((char *)vars1.user) + 1; - if (capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) { + if (vars1.capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) { uint64_t passlen64; int pass_len_enc=mysql_decode_length(pkt,&passlen64); - pass_len = passlen64; + vars1.pass_len = passlen64; pkt += pass_len_enc; - if (pass_len > (len - (pkt - _ptr))) { + if (vars1.pass_len > (len - (pkt - vars1._ptr))) { ret = false; - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . goto __exit_process_pkt_handshake_response\n", (*myds), (*myds)->sess, user); - goto __exit_process_pkt_handshake_response; + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . goto __exit_process_pkt_handshake_response\n", (*myds), (*myds)->sess, vars1.user); + return false; } } else { - pass_len = (capabilities & CLIENT_SECURE_CONNECTION ? *pkt++ : strlen((char *)pkt)); - if (pass_len > (len - (pkt - _ptr))) { + vars1.pass_len = (vars1.capabilities & CLIENT_SECURE_CONNECTION ? *pkt++ : strlen((char *)pkt)); + if (vars1.pass_len > (len - (pkt - vars1._ptr))) { ret = false; - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . goto __exit_process_pkt_handshake_response\n", (*myds), (*myds)->sess, user); - goto __exit_process_pkt_handshake_response; + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . goto __exit_process_pkt_handshake_response\n", (*myds), (*myds)->sess, vars1.user); + return false; } } - pass = (unsigned char *)malloc(pass_len+1); - memcpy(pass, pkt, pass_len); - pass[pass_len] = 0; + vars1.pass = (unsigned char *)malloc(vars1.pass_len+1); + memcpy(vars1.pass, pkt, vars1.pass_len); + vars1.pass[vars1.pass_len] = 0; - pkt += pass_len; - if (capabilities & CLIENT_CONNECT_WITH_DB) { - unsigned int remaining = len - (pkt - _ptr); - db_tmp = strndup((const char *)pkt, remaining); - if (db_tmp) { - db = db_tmp; + pkt += vars1.pass_len; + if (vars1.capabilities & CLIENT_CONNECT_WITH_DB) { + unsigned int remaining = len - (pkt - vars1._ptr); + vars1.db_tmp = strndup((const char *)pkt, remaining); + if (vars1.db_tmp) { + vars1.db = vars1.db_tmp; } pkt++; - if (db) { - pkt+=strlen(db); + if (vars1.db) { + pkt+=strlen(vars1.db); } } else { - db = NULL; + vars1.db = NULL; } - if (pass_len) { - if (pass[pass_len-1] == 0) { - pass_len--; // remove the extra 0 if present + if (vars1.pass_len) { + if (vars1.pass[vars1.pass_len-1] == 0) { + vars1.pass_len--; // remove the extra 0 if present } } - if (_ptr+len > pkt) { - if (capabilities & CLIENT_PLUGIN_AUTH) { - auth_plugin = pkt; + if (vars1._ptr+len > pkt) { + if (vars1.capabilities & CLIENT_PLUGIN_AUTH) { + vars1.auth_plugin = pkt; } } - if (auth_plugin == NULL) { - auth_plugin = (unsigned char *)"mysql_native_password"; // default + return true; +} + +void MySQL_Protocol::PPHR_3(MyProt_tmp_auth_vars& vars1) { // detect plugin id + if (vars1.auth_plugin == NULL) { + vars1.auth_plugin = (unsigned char *)"mysql_native_password"; // default auth_plugin_id = 1; } + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' , auth_plugin_id=%d\n", (*myds), (*myds)->sess, vars1.user, auth_plugin_id); - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' , auth_plugin_id=%d\n", (*myds), (*myds)->sess, user, auth_plugin_id); if (auth_plugin_id == 0) { - if (strncmp((char *)auth_plugin,(char *)"mysql_native_password",strlen((char *)"mysql_native_password"))==0) { + if (strncmp((char *)vars1.auth_plugin,plugins[0],strlen(plugins[0]))==0) { // mysql_native_password auth_plugin_id = 1; + } else if (strncmp((char *)vars1.auth_plugin,plugins[1],strlen(plugins[1]))==0) { // mysql_clear_password + auth_plugin_id = 2; + } else if (strncmp((char *)vars1.auth_plugin,plugins[2],strlen(plugins[2]))==0) { // caching_sha2_password + //auth_plugin_id = 3; // FIXME: this is temporary, because yet not supported + auth_plugin_id = 0; // FIXME: this is temporary, because yet not supported . It must become 3 } } - if (auth_plugin_id == 0) { - if (strncmp((char *)auth_plugin,(char *)"mysql_clear_password",strlen((char *)"mysql_clear_password"))==0) { - auth_plugin_id = 2; + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' , auth_plugin_id=%d\n", (*myds), (*myds)->sess, vars1.user, auth_plugin_id); +} + +bool MySQL_Protocol::PPHR_4auth0(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1) { + if ((*myds)->switching_auth_stage == 0) { + (*myds)->switching_auth_stage = 1; + (*myds)->auth_in_progress = 1; + // check if user exists + bool user_exists = true; + if (GloMyLdapAuth) { // we check if user exists only if GloMyLdapAuth is enabled +#ifdef PROXYSQLCLICKHOUSE + enum proxysql_session_type session_type = (*myds)->sess->session_type; + if (session_type == PROXYSQL_SESSION_CLICKHOUSE) { + //user_exists = GloClickHouseAuth->exists((char *)user); + // for clickhouse, we currently do not support clear text or LDAP + user_exists = true; + } else { +#endif /* PROXYSQLCLICKHOUSE */ + user_exists = GloMyAuth->exists((char *)vars1.user); + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user_exists=%d , user='%s'\n", (*myds), (*myds)->sess, user_exists, vars1.user); + //password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass); +#ifdef PROXYSQLCLICKHOUSE + } +#endif /* PROXYSQLCLICKHOUSE */ + } + if (user_exists) { + (*myds)->switching_auth_type = 1; // mysql_native_password + } else { + (*myds)->switching_auth_type = 2; // mysql_clear_password } + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user_exists=%d , user='%s' , setting switching_auth_type=%d\n", (*myds), (*myds)->sess, user_exists, vars1.user, (*myds)->switching_auth_type); + generate_pkt_auth_switch_request(true, NULL, NULL); + (*myds)->myconn->userinfo->set((char *)vars1.user, NULL, vars1.db, NULL); + ret = false; + return false; } -//__switch_auth_plugin: - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' , auth_plugin_id=%d\n", (*myds), (*myds)->sess, user, auth_plugin_id); - if (auth_plugin_id == 0) { + return true; +} + + +bool MySQL_Protocol::PPHR_4auth1(unsigned char *pkt, unsigned int len, bool& ret, MyProt_tmp_auth_vars& vars1) { + if (GloMyLdapAuth) { if ((*myds)->switching_auth_stage == 0) { - (*myds)->switching_auth_stage = 1; - // check if user exists bool user_exists = true; - if (GloMyLdapAuth) { // we check if user exists only if GloMyLdapAuth is enabled #ifdef PROXYSQLCLICKHOUSE - if (session_type == PROXYSQL_SESSION_CLICKHOUSE) { - //user_exists = GloClickHouseAuth->exists((char *)user); - // for clickhouse, we currently do not support clear text or LDAP - user_exists = true; - } else { + enum proxysql_session_type session_type = (*myds)->sess->session_type; + if (session_type == PROXYSQL_SESSION_CLICKHOUSE) { + //user_exists = GloClickHouseAuth->exists((char *)user); + // for clickhouse, we currently do not support clear text or LDAP + user_exists = true; + } else { #endif /* PROXYSQLCLICKHOUSE */ - user_exists = GloMyAuth->exists((char *)user); - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user_exists=%d , user='%s'\n", (*myds), (*myds)->sess, user_exists, user); - //password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass); + user_exists = GloMyAuth->exists((char *)vars1.user); + //password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass); #ifdef PROXYSQLCLICKHOUSE - } -#endif /* PROXYSQLCLICKHOUSE */ } - if (user_exists) { - (*myds)->switching_auth_type = 1; // mysql_native_password - } else { +#endif /* PROXYSQLCLICKHOUSE */ + if (user_exists == false) { (*myds)->switching_auth_type = 2; // mysql_clear_password + (*myds)->switching_auth_stage = 1; + (*myds)->auth_in_progress = 1; + generate_pkt_auth_switch_request(true, NULL, NULL); + (*myds)->myconn->userinfo->set((char *)vars1.user, NULL, vars1.db, NULL); + ret = false; + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . goto __exit_process_pkt_handshake_response. User does not exist\n", (*myds), (*myds)->sess, vars1.user); + return false; } - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user_exists=%d , user='%s' , setting switching_auth_type=%d\n", (*myds), (*myds)->sess, user_exists, user, (*myds)->switching_auth_type); - generate_pkt_auth_switch_request(true, NULL, NULL); - (*myds)->myconn->userinfo->set((char *)user, NULL, db, NULL); - ret = false; - goto __exit_process_pkt_handshake_response; + } + } + return true; +} + +void MySQL_Protocol::PPHR_5passwordTrue( + unsigned char *pkt, unsigned int len, + bool& ret, + MyProt_tmp_auth_vars& vars1, + char * reply, + MyProt_tmp_auth_attrs& attr1) { + //int& default_hostgroup, char *& default_schema, char *& attributes, bool& schema_locked, bool& transaction_persistent, bool& fast_forward, int& max_connections) { +#ifdef DEBUG + char *tmp_pass=strdup(vars1.password); + int lpass = strlen(tmp_pass); + for (int i=2; isess, vars1.user, tmp_pass); + free(tmp_pass); +#endif // debug + (*myds)->sess->default_hostgroup = attr1.default_hostgroup; + (*myds)->sess->default_schema = attr1.default_schema; // just the pointer is passed + (*myds)->sess->user_attributes = attr1.attributes; // just the pointer is passed +#ifdef DEBUG + debug_spiffe_id(vars1.user,attr1.attributes, __LINE__, __func__); +#endif + (*myds)->sess->schema_locked = attr1.schema_locked; + (*myds)->sess->transaction_persistent = attr1.transaction_persistent; + (*myds)->sess->session_fast_forward=false; // default + if ((*myds)->sess->session_type == PROXYSQL_SESSION_MYSQL) { + (*myds)->sess->session_fast_forward = attr1.fast_forward; + } + (*myds)->sess->user_max_connections = attr1.max_connections; +} + + +void MySQL_Protocol::PPHR_5passwordFalse_0( + unsigned char *pkt, unsigned int len, + bool& ret, + MyProt_tmp_auth_vars& vars1, + char * reply, + MyProt_tmp_auth_attrs& attr1) { + if (strcmp((const char *)vars1.user,mysql_thread___monitor_username)==0) { + proxy_scramble(reply, (*myds)->myconn->scramble_buff, mysql_thread___monitor_password); + if (memcmp(reply, vars1.pass, SHA_DIGEST_LENGTH)==0) { + (*myds)->sess->default_hostgroup=STATS_HOSTGROUP; + (*myds)->sess->default_schema=strdup((char *)"main"); // just the pointer is passed + (*myds)->sess->schema_locked=false; + (*myds)->sess->transaction_persistent=false; + (*myds)->sess->session_fast_forward=false; + (*myds)->sess->user_max_connections=0; + vars1.password=l_strdup(mysql_thread___monitor_password); + ret=true; } } else { - if (auth_plugin_id == 1) { - if (GloMyLdapAuth) { - if ((*myds)->switching_auth_stage == 0) { - bool user_exists = true; -#ifdef PROXYSQLCLICKHOUSE - if (session_type == PROXYSQL_SESSION_CLICKHOUSE) { - //user_exists = GloClickHouseAuth->exists((char *)user); - // for clickhouse, we currently do not support clear text or LDAP - user_exists = true; + ret=false; + } +} + +void MySQL_Protocol::PPHR_5passwordFalse_auth2( + unsigned char *pkt, unsigned int len, + bool& ret, + MyProt_tmp_auth_vars& vars1, + char * reply, + MyProt_tmp_auth_attrs& attr1, + void *& sha1_pass) { + if (GloMyLdapAuth) { +#ifdef DEBUG + { + char *tmp_pass=strdup((const char *)vars1.pass); + int lpass = strlen(tmp_pass); + for (int i=2; isess, vars1.user, tmp_pass); + free(tmp_pass); + } +#endif // debug + char *backend_username = NULL; + (*myds)->sess->use_ldap_auth = true; + vars1.password = GloMyLdapAuth->lookup((char *) vars1.user, (char *) vars1.pass, USERNAME_FRONTEND, + &attr1._ret_use_ssl, &attr1.default_hostgroup, &attr1.default_schema, &attr1.schema_locked, + &attr1.transaction_persistent, &attr1.fast_forward, &attr1.max_connections, &sha1_pass, &attr1.attributes, &backend_username); + if (vars1.password) { +#ifdef DEBUG + char *tmp_pass=strdup(vars1.password); + int lpass = strlen(tmp_pass); + for (int i=2; isess, backend_username, tmp_pass); + free(tmp_pass); +#endif // debug + (*myds)->sess->default_hostgroup=attr1.default_hostgroup; + (*myds)->sess->default_schema=attr1.default_schema; // just the pointer is passed + (*myds)->sess->user_attributes = attr1.attributes; // just the pointer is passed, LDAP returns empty string +#ifdef DEBUG + debug_spiffe_id(vars1.user,attr1.attributes, __LINE__, __func__); +#endif + (*myds)->sess->schema_locked=attr1.schema_locked; + (*myds)->sess->transaction_persistent=attr1.transaction_persistent; + (*myds)->sess->session_fast_forward=attr1.fast_forward; + (*myds)->sess->user_max_connections=attr1.max_connections; + if (strcmp(vars1.password, (char *) vars1.pass) == 0) { + if (backend_username) { + free(vars1.password); + vars1.password=NULL; + vars1.password=GloMyAuth->lookup(backend_username, USERNAME_BACKEND, &attr1._ret_use_ssl, &attr1.default_hostgroup, &attr1.default_schema, &attr1.schema_locked, &attr1.transaction_persistent, &attr1.fast_forward, &attr1.max_connections, &sha1_pass, &attr1.attributes); + if (vars1.password) { + (*myds)->sess->default_hostgroup=attr1.default_hostgroup; + // Free the previously set 'default_schema' by 'GloMyLdapAuth' + if ((*myds)->sess->default_schema) { + free((*myds)->sess->default_schema); + } + (*myds)->sess->default_schema=attr1.default_schema; // just the pointer is passed + // Free the previously set 'user_attributes' by 'GloMyLdapAuth' + if ((*myds)->sess->user_attributes) { + free((*myds)->sess->user_attributes); + } + (*myds)->sess->user_attributes = attr1.attributes; // just the pointer is passed +#ifdef DEBUG + proxy_info("Attributes for user %s: %s\n" , vars1.user, attr1.attributes); +#endif + (*myds)->sess->schema_locked=attr1.schema_locked; + (*myds)->sess->transaction_persistent=attr1.transaction_persistent; + (*myds)->sess->session_fast_forward=attr1.fast_forward; + (*myds)->sess->user_max_connections=attr1.max_connections; + char *tmp_user=strdup((const char *)vars1.user); + userinfo->set(backend_username, NULL, NULL, NULL); + // 'MySQL_Connection_userinfo::set' duplicates the supplied information, 'free' is required. + free(backend_username); + if (sha1_pass==NULL) { + // currently proxysql doesn't know any sha1_pass for that specific user, let's set it! + GloMyAuth->set_SHA1((char *)userinfo->username, USERNAME_FRONTEND,reply); + } + if (userinfo->sha1_pass) free(userinfo->sha1_pass); + userinfo->sha1_pass=sha1_pass_hex(reply); + userinfo->fe_username=strdup((const char *)tmp_user); + free(tmp_user); + ret=true; } else { -#endif /* PROXYSQLCLICKHOUSE */ - user_exists = GloMyAuth->exists((char *)user); - //password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass); -#ifdef PROXYSQLCLICKHOUSE - } -#endif /* PROXYSQLCLICKHOUSE */ - if (user_exists == false) { - (*myds)->switching_auth_type = 2; // mysql_clear_password - (*myds)->switching_auth_stage = 1; - generate_pkt_auth_switch_request(true, NULL, NULL); - (*myds)->myconn->userinfo->set((char *)user, NULL, db, NULL); - ret = false; - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . goto __exit_process_pkt_handshake_response. User does not exist\n", (*myds), (*myds)->sess, user); - goto __exit_process_pkt_handshake_response; + proxy_error("Unable to load credentials for backend user %s , associated to LDAP user %s\n", backend_username, vars1.user); } + } else { + proxy_error("Unable to find backend user associated to LDAP user '%s'\n", vars1.user); + ret=false; } } } } +} + +void MySQL_Protocol::PPHR_7auth1( + unsigned char *pkt, unsigned int len, + bool& ret, + MyProt_tmp_auth_vars& vars1, + char * reply, + MyProt_tmp_auth_attrs& attr1, + void *& sha1_pass) { + enum proxysql_session_type session_type = (*myds)->sess->session_type; + if (session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_SQLITE || session_type == PROXYSQL_SESSION_ADMIN || session_type == PROXYSQL_SESSION_STATS) { + ret=proxy_scramble_sha1((char *)vars1.pass,(*myds)->myconn->scramble_buff,vars1.password+1, reply); + if (ret) { + if (sha1_pass==NULL) { + // currently proxysql doesn't know any sha1_pass for that specific user, let's set it! + GloMyAuth->set_SHA1((char *)vars1.user, USERNAME_FRONTEND,reply); + } + if (userinfo->sha1_pass) + free(userinfo->sha1_pass); + userinfo->sha1_pass=sha1_pass_hex(reply); + } + } +} + + +void MySQL_Protocol::PPHR_7auth2( + unsigned char *pkt, unsigned int len, + bool& ret, + MyProt_tmp_auth_vars& vars1, + char * reply, + MyProt_tmp_auth_attrs& attr1, + void *& sha1_pass) { + enum proxysql_session_type session_type = (*myds)->sess->session_type; + if (session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_SQLITE || session_type == PROXYSQL_SESSION_ADMIN || session_type == PROXYSQL_SESSION_STATS) { + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , username='%s' , session_type=%d\n", (*myds), (*myds)->sess, vars1.user, session_type); + uint8_t hash_stage1[SHA_DIGEST_LENGTH]; + uint8_t hash_stage2[SHA_DIGEST_LENGTH]; + SHA_CTX sha1_context; + SHA1_Init(&sha1_context); + SHA1_Update(&sha1_context, vars1.pass, vars1.pass_len); + SHA1_Final(hash_stage1, &sha1_context); + SHA1_Init(&sha1_context); + SHA1_Update(&sha1_context,hash_stage1,SHA_DIGEST_LENGTH); + SHA1_Final(hash_stage2, &sha1_context); + char *double_hashed_password = sha1_pass_hex((char *)hash_stage2); // note that sha1_pass_hex() returns a new buffer + if (strcasecmp(double_hashed_password,vars1.password)==0) { + ret = true; + if (sha1_pass==NULL) { + // currently proxysql doesn't know any sha1_pass for that specific user, let's set it! + GloMyAuth->set_SHA1((char *)vars1.user, USERNAME_FRONTEND,hash_stage1); + } + if (userinfo->sha1_pass) + free(userinfo->sha1_pass); + userinfo->sha1_pass=sha1_pass_hex((char *)hash_stage1); + } else { + ret = false; + } + free(double_hashed_password); + } +} + +void MySQL_Protocol::PPHR_SetConnAttrs(MyProt_tmp_auth_vars& vars1, MyProt_tmp_auth_attrs& attr1) { + MySQL_Connection *myconn = NULL; + myconn=sess->client_myds->myconn; + assert(myconn); + myconn->set_charset(vars1.charset, CONNECT_START); + + std::stringstream ss; + ss << vars1.charset; + + /* We are processing handshake from client. Client sends us a character set it will use in communication. + * we store this character set in the client's variables to use later in multiplexing with different backends + */ + mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_RESULTS, ss.str().c_str()); + mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_CLIENT, ss.str().c_str()); + mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_CONNECTION, ss.str().c_str()); + mysql_variables.client_set_value(sess, SQL_COLLATION_CONNECTION, ss.str().c_str()); + + // enable compression + if (vars1.capabilities & CLIENT_COMPRESS) { + if (myconn->options.server_capabilities & CLIENT_COMPRESS) { + myconn->options.compression_min_length=50; + //myconn->set_status_compression(true); // don't enable this here. It needs to be enabled after the OK is sent + } + } + if (attr1._ret_use_ssl==true) { + (*myds)->sess->use_ssl = true; + } +} + +/** + * @brief Process handshake response from the client, and it needs to be called until + * the authentication is completed (successfully or failed) + * + * @return: + * true: the authentication completed + * false: the authentication failed, or more data is needed + */ +bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned int len) { +#ifdef DEBUG + if (dump_pkt) { __dump_pkt(__func__,pkt,len); } +#endif + bool ret = false; + auth_plugin_id = 0; + + char reply[SHA_DIGEST_LENGTH+1] = { 0 }; + enum proxysql_session_type session_type = (*myds)->sess->session_type; + + void *sha1_pass=NULL; + MyProt_tmp_auth_vars vars1; + MyProt_tmp_auth_attrs attr1; + vars1._ptr = pkt; + mysql_hdr hdr; + bool bool_rc = false; + memcpy(&hdr,pkt,sizeof(mysql_hdr)); + //Copy4B(&hdr,pkt); + pkt += sizeof(mysql_hdr); + + // NOTE: 'mysqlsh' sends a 'COM_INIT_DB' as soon as the connection is openned + // before ProxySQL has sent 'Server Greeting' messsage. Because this packet is + // unexpected, we simple return 'false' and exit. + if (hdr.pkt_id == 0 && *pkt == 2) { + ret = false; + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . Client is disconnecting\n", (*myds), (*myds)->sess, vars1.user); + goto __exit_process_pkt_handshake_response; + } + + if ((*myds)->myconn->userinfo->username) { // authentication already started. + int rc = PPHR_1(pkt, len, ret, vars1); + if (rc == 1) + goto __exit_process_pkt_handshake_response; + if (rc == 2) + goto __do_auth; + assert(0); + } + + bool_rc = PPHR_2(pkt, len, ret, vars1); + if (bool_rc == false) + goto __exit_process_pkt_handshake_response; + + + PPHR_3(vars1); // detect plugin id + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' , auth_plugin_id=%d\n", (*myds), (*myds)->sess, vars1.user, auth_plugin_id); + + if (auth_plugin_id == 0) { + bool_rc = PPHR_4auth0(pkt, len, ret, vars1); + if (bool_rc == false) + goto __exit_process_pkt_handshake_response; + } else if (auth_plugin_id == 1) { + bool_rc = PPHR_4auth1(pkt, len, ret, vars1); + if (bool_rc == false) + goto __exit_process_pkt_handshake_response; + } else if (auth_plugin_id == 2) { // caching_sha2_password + } if (auth_plugin_id == 0) { // unknown plugin + // this code is probably never executed, because PPHR_4auth0 will handle auth_plugin_id==0 ret = false; - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . goto __exit_process_pkt_handshake_response . Unknown auth plugin\n", (*myds), (*myds)->sess, user); + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . goto __exit_process_pkt_handshake_response . Unknown auth plugin\n", (*myds), (*myds)->sess, vars1.user); goto __exit_process_pkt_handshake_response; } - //char reply[SHA_DIGEST_LENGTH+1]; - //reply[SHA_DIGEST_LENGTH]='\0'; - //int default_hostgroup=-1; - //char *default_schema=NULL; - //bool schema_locked; - //bool transaction_persistent = true; - //bool fast_forward = false; - //int max_connections; - //enum proxysql_session_type session_type = (*myds)->sess->session_type; __do_auth: - { // reject connections from unknown charsets - const MARIADB_CHARSET_INFO * c = proxysql_find_charset_nr(charset); + const MARIADB_CHARSET_INFO * c = proxysql_find_charset_nr(vars1.charset); if (!c) { - proxy_error("Client %s:%d is trying to use unknown charset %u. Disconnecting\n", (*myds)->addr.addr, (*myds)->addr.port, charset); - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . Client %s:%d is trying to use unknown charset %u. Disconnecting\n", (*myds), (*myds)->sess, user, (*myds)->addr.addr, (*myds)->addr.port, charset); + proxy_error("Client %s:%d is trying to use unknown charset %u. Disconnecting\n", (*myds)->addr.addr, (*myds)->addr.port, vars1.charset); + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , user='%s' . Client %s:%d is trying to use unknown charset %u. Disconnecting\n", (*myds), (*myds)->sess, vars1.user, (*myds)->addr.addr, (*myds)->addr.port, vars1.charset); ret = false; goto __exit_do_auth; } // set the default session charset - (*myds)->sess->default_charset = charset; + (*myds)->sess->default_charset = vars1.charset; } if (session_type == PROXYSQL_SESSION_CLICKHOUSE) { #ifdef PROXYSQLCLICKHOUSE - password=GloClickHouseAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass); + vars1.password=GloClickHouseAuth->lookup((char *)vars1.user, USERNAME_FRONTEND, &attr1._ret_use_ssl, &attr1.default_hostgroup, &attr1.default_schema, &attr1.schema_locked, &attr1.transaction_persistent, &attr1.fast_forward, &attr1.max_connections, &sha1_pass); #endif /* PROXYSQLCLICKHOUSE */ } else { - password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass, &attributes); + vars1.password=GloMyAuth->lookup((char *)vars1.user, USERNAME_FRONTEND, &attr1._ret_use_ssl, &attr1.default_hostgroup, &attr1.default_schema, &attr1.schema_locked, &attr1.transaction_persistent, &attr1.fast_forward, &attr1.max_connections, &sha1_pass, &attr1.attributes); } //assert(default_hostgroup>=0); - if (password) { -#ifdef DEBUG - char *tmp_pass=strdup(password); - int lpass = strlen(tmp_pass); - for (int i=2; isess, user, tmp_pass); - free(tmp_pass); -#endif // debug - (*myds)->sess->default_hostgroup=default_hostgroup; - (*myds)->sess->default_schema=default_schema; // just the pointer is passed - (*myds)->sess->user_attributes = attributes; // just the pointer is passed -#ifdef DEBUG - debug_spiffe_id(user,attributes, __LINE__, __func__); -#endif - (*myds)->sess->schema_locked=schema_locked; - (*myds)->sess->transaction_persistent=transaction_persistent; - (*myds)->sess->session_fast_forward=false; // default - if ((*myds)->sess->session_type == PROXYSQL_SESSION_MYSQL) { - (*myds)->sess->session_fast_forward=fast_forward; - } - (*myds)->sess->user_max_connections=max_connections; + if (vars1.password) { + PPHR_5passwordTrue(pkt, len, ret, vars1, reply, attr1); } - if (password == NULL) { + if (vars1.password == NULL) { // this is a workaround for bug #603 if ( ((*myds)->sess->session_type == PROXYSQL_SESSION_ADMIN) @@ -1964,186 +2218,48 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned ((*myds)->sess->session_type == PROXYSQL_SESSION_SQLITE) //#endif // TEST_AURORA || TEST_GALERA ) { - if (strcmp((const char *)user,mysql_thread___monitor_username)==0) { - proxy_scramble(reply, (*myds)->myconn->scramble_buff, mysql_thread___monitor_password); - if (memcmp(reply, pass, SHA_DIGEST_LENGTH)==0) { - (*myds)->sess->default_hostgroup=STATS_HOSTGROUP; - (*myds)->sess->default_schema=strdup((char *)"main"); // just the pointer is passed - (*myds)->sess->schema_locked=false; - (*myds)->sess->transaction_persistent=false; - (*myds)->sess->session_fast_forward=false; - (*myds)->sess->user_max_connections=0; - password=l_strdup(mysql_thread___monitor_password); - ret=true; - } - } else { - ret=false; - } + PPHR_5passwordFalse_0(pkt, len, ret, vars1, reply, attr1); } else { ret=false; // by default, assume this will fail // try LDAP if (auth_plugin_id==2) { - if (GloMyLdapAuth) { -#ifdef DEBUG - { - char *tmp_pass=strdup((const char *)pass); - int lpass = strlen(tmp_pass); - for (int i=2; isess, user, tmp_pass); - free(tmp_pass); - } -#endif // debug - char *backend_username = NULL; - (*myds)->sess->use_ldap_auth = true; - password = GloMyLdapAuth->lookup((char *) user, (char *) pass, USERNAME_FRONTEND, - &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, - &transaction_persistent, &fast_forward, &max_connections, &sha1_pass, &attributes, &backend_username); - if (password) { -#ifdef DEBUG - char *tmp_pass=strdup(password); - int lpass = strlen(tmp_pass); - for (int i=2; isess, backend_username, tmp_pass); - free(tmp_pass); -#endif // debug - (*myds)->sess->default_hostgroup=default_hostgroup; - (*myds)->sess->default_schema=default_schema; // just the pointer is passed - (*myds)->sess->user_attributes = attributes; // just the pointer is passed, LDAP returns empty string -#ifdef DEBUG - debug_spiffe_id(user,attributes, __LINE__, __func__); -#endif - (*myds)->sess->schema_locked=schema_locked; - (*myds)->sess->transaction_persistent=transaction_persistent; - (*myds)->sess->session_fast_forward=fast_forward; - (*myds)->sess->user_max_connections=max_connections; - if (strcmp(password, (char *) pass) == 0) { - if (backend_username) { - free(password); - password=NULL; - password=GloMyAuth->lookup(backend_username, USERNAME_BACKEND, &_ret_use_ssl, &default_hostgroup, &default_schema, &schema_locked, &transaction_persistent, &fast_forward, &max_connections, &sha1_pass, &attributes); - if (password) { - (*myds)->sess->default_hostgroup=default_hostgroup; - // Free the previously set 'default_schema' by 'GloMyLdapAuth' - if ((*myds)->sess->default_schema) { - free((*myds)->sess->default_schema); - } - (*myds)->sess->default_schema=default_schema; // just the pointer is passed - // Free the previously set 'user_attributes' by 'GloMyLdapAuth' - if ((*myds)->sess->user_attributes) { - free((*myds)->sess->user_attributes); - } - (*myds)->sess->user_attributes = attributes; // just the pointer is passed -#ifdef DEBUG - proxy_info("Attributes for user %s: %s\n" , user, attributes); -#endif - (*myds)->sess->schema_locked=schema_locked; - (*myds)->sess->transaction_persistent=transaction_persistent; - (*myds)->sess->session_fast_forward=fast_forward; - (*myds)->sess->user_max_connections=max_connections; - char *tmp_user=strdup((const char *)user); - userinfo->set(backend_username, NULL, NULL, NULL); - // 'MySQL_Connection_userinfo::set' duplicates the supplied information, 'free' is required. - free(backend_username); - if (sha1_pass==NULL) { - // currently proxysql doesn't know any sha1_pass for that specific user, let's set it! - GloMyAuth->set_SHA1((char *)userinfo->username, USERNAME_FRONTEND,reply); - } - if (userinfo->sha1_pass) free(userinfo->sha1_pass); - userinfo->sha1_pass=sha1_pass_hex(reply); - userinfo->fe_username=strdup((const char *)tmp_user); - free(tmp_user); - ret=true; - } else { - proxy_error("Unable to load credentials for backend user %s , associated to LDAP user %s\n", backend_username, user); - } - } else { - proxy_error("Unable to find backend user associated to LDAP user '%s'\n", user); - ret=false; - } - } - } - } + PPHR_5passwordFalse_auth2(pkt, len, ret, vars1, reply, attr1, sha1_pass); } } } else { - if (pass_len==0 && strlen(password)==0) { + if (vars1.pass_len==0 && strlen(vars1.password)==0) { ret=true; - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , username='%s' , password=''\n", (*myds), (*myds)->sess, user); + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , username='%s' , password=''\n", (*myds), (*myds)->sess, vars1.user); } else { #ifdef DEBUG - char *tmp_pass=strdup(password); + char *tmp_pass=strdup(vars1.password); int lpass = strlen(tmp_pass); for (int i=2; isess, user, tmp_pass, auth_plugin_id); + proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , username='%s' , password='%s' , auth_plugin_id=%d\n", (*myds), (*myds)->sess, vars1.user, tmp_pass, auth_plugin_id); free(tmp_pass); #endif // debug - if (password[0]!='*') { // clear text password + if (vars1.password[0]!='*') { // clear text password if (auth_plugin_id == 1) { // mysql_native_password - proxy_scramble(reply, (*myds)->myconn->scramble_buff, password); - if (pass_len != 0 && memcmp(reply, pass, SHA_DIGEST_LENGTH)==0) { + proxy_scramble(reply, (*myds)->myconn->scramble_buff, vars1.password); + if (vars1.pass_len != 0 && memcmp(reply, vars1.pass, SHA_DIGEST_LENGTH)==0) { ret=true; } - } else { // mysql_clear_password - if (strcmp(password, (char *) pass) == 0) { + } else if (auth_plugin_id == 2) { // mysql_clear_password + if (strcmp(vars1.password, (char *) vars1.pass) == 0) { ret = true; } + } else { + assert(0); } } else { if (auth_plugin_id == 1) { - if (session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_SQLITE || session_type == PROXYSQL_SESSION_ADMIN || session_type == PROXYSQL_SESSION_STATS) { - ret=proxy_scramble_sha1((char *)pass,(*myds)->myconn->scramble_buff,password+1, reply); - if (ret) { - if (sha1_pass==NULL) { - // currently proxysql doesn't know any sha1_pass for that specific user, let's set it! - GloMyAuth->set_SHA1((char *)user, USERNAME_FRONTEND,reply); - } - if (userinfo->sha1_pass) - free(userinfo->sha1_pass); - userinfo->sha1_pass=sha1_pass_hex(reply); - } - } - } else { // mysql_clear_password - if (session_type == PROXYSQL_SESSION_MYSQL || session_type == PROXYSQL_SESSION_SQLITE || session_type == PROXYSQL_SESSION_ADMIN || session_type == PROXYSQL_SESSION_STATS) { -/* - char sha1_2[SHA_DIGEST_LENGTH+1]; - sha1_2[SHA_DIGEST_LENGTH]='\0'; - proxy_compute_sha1_hash((unsigned char *)reply,(char *)pass,pass_len); - proxy_compute_sha1_hash((unsigned char *)sha1_2,reply,strlen(reply)); - uint8 hash_stage2[SHA_DIGEST_LENGTH]; - unhex_pass(hash_stage2,sha1_2); -*/ - proxy_debug(PROXY_DEBUG_MYSQL_AUTH, 5, "Session=%p , DS=%p , username='%s' , session_type=%d\n", (*myds), (*myds)->sess, user, session_type); - uint8_t hash_stage1[SHA_DIGEST_LENGTH]; - uint8_t hash_stage2[SHA_DIGEST_LENGTH]; - SHA_CTX sha1_context; - SHA1_Init(&sha1_context); - SHA1_Update(&sha1_context, pass, pass_len); - SHA1_Final(hash_stage1, &sha1_context); - SHA1_Init(&sha1_context); - SHA1_Update(&sha1_context,hash_stage1,SHA_DIGEST_LENGTH); - SHA1_Final(hash_stage2, &sha1_context); - char *double_hashed_password = sha1_pass_hex((char *)hash_stage2); // note that sha1_pass_hex() returns a new buffer - - if (strcasecmp(double_hashed_password,password)==0) { - ret = true; - if (sha1_pass==NULL) { - // currently proxysql doesn't know any sha1_pass for that specific user, let's set it! - GloMyAuth->set_SHA1((char *)user, USERNAME_FRONTEND,hash_stage1); - } - if (userinfo->sha1_pass) - free(userinfo->sha1_pass); - userinfo->sha1_pass=sha1_pass_hex((char *)hash_stage1); - } else { - ret = false; - } - free(double_hashed_password); - } + PPHR_7auth1(pkt, len, ret, vars1, reply, attr1, sha1_pass); + } else if (auth_plugin_id == 2) { // mysql_clear_password + PPHR_7auth2(pkt, len, ret, vars1, reply, attr1, sha1_pass); + } else { + assert(0); } } } @@ -2151,60 +2267,33 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned __exit_do_auth: - if (_ret_use_ssl==true) { - (*myds)->sess->use_ssl = true; - } -// if (_ret_use_ssl==true) { -// // if we reached here, use_ssl is false , but _ret_use_ssl is true -// // it means that a client is required to use SSL , but it is not -// ret=false; -// } -// } #ifdef DEBUG { char *tmp_pass= NULL; - if (password) { - tmp_pass = strdup(password); + if (vars1.password) { + tmp_pass = strdup(vars1.password); int lpass = strlen(tmp_pass); for (int i=2; i, capabilities:%u char:%u, use_ssl:%s\n", - (capabilities & CLIENT_SECURE_CONNECTION ? "new" : "old"), user, tmp_pass, db, max_pkt, capabilities, charset, ((*myds)->encrypted ? "yes" : "no")); + (vars1.capabilities & CLIENT_SECURE_CONNECTION ? "new" : "old"), vars1.user, tmp_pass, vars1.db, vars1.max_pkt, vars1.capabilities, vars1.charset, ((*myds)->encrypted ? "yes" : "no")); free(tmp_pass); } #endif assert(sess); assert(sess->client_myds); - myconn=sess->client_myds->myconn; - assert(myconn); - myconn->set_charset(charset, CONNECT_START); - { - std::stringstream ss; - ss << charset; - /* We are processing handshake from client. Client sends us a character set it will use in communication. - * we store this character set in the client's variables to use later in multiplexing with different backends - */ - mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_RESULTS, ss.str().c_str()); - mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_CLIENT, ss.str().c_str()); - mysql_variables.client_set_value(sess, SQL_CHARACTER_SET_CONNECTION, ss.str().c_str()); - mysql_variables.client_set_value(sess, SQL_COLLATION_CONNECTION, ss.str().c_str()); - } - // enable compression - if (capabilities & CLIENT_COMPRESS) { - if (myconn->options.server_capabilities & CLIENT_COMPRESS) { - myconn->options.compression_min_length=50; - //myconn->set_status_compression(true); // don't enable this here. It needs to be enabled after the OK is sent - } - } + // set connection attributes (charsets, compression, encryption) + PPHR_SetConnAttrs(vars1, attr1); + #ifdef DEBUG - if (dump_pkt) { __dump_pkt(__func__,_ptr,len); } + if (dump_pkt) { __dump_pkt(__func__,vars1._ptr,len); } #endif - if (use_ssl) { + if (vars1.use_ssl) { ret=true; goto __exit_process_pkt_handshake_response; } @@ -2214,33 +2303,33 @@ bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned (*myds)->DSS=STATE_CLIENT_HANDSHAKE; if (!userinfo->username) // if set already, ignore - userinfo->username=strdup((const char *)user); - userinfo->password=strdup((const char *)password); - if (db) userinfo->set_schemaname(db,strlen(db)); + userinfo->username=strdup((const char *)vars1.user); + userinfo->password=strdup((const char *)vars1.password); + if (vars1.db) userinfo->set_schemaname(vars1.db,strlen(vars1.db)); } else { // we always duplicate username and password, or crashes happen if (!userinfo->username) // if set already, ignore - userinfo->username=strdup((const char *)user); - if (pass_len) userinfo->password=strdup((const char *)""); + userinfo->username=strdup((const char *)vars1.user); + if (vars1.pass_len) userinfo->password=strdup((const char *)""); } userinfo->set(NULL,NULL,NULL,NULL); // just to call compute_hash() __exit_process_pkt_handshake_response: - free(pass); - if (password) { - free(password); - password=NULL; + free(vars1.pass); + if (vars1.password) { + free(vars1.password); + vars1.password=NULL; } if (sha1_pass) { free(sha1_pass); sha1_pass=NULL; } - if (db_tmp) { - free(db_tmp); - db_tmp=NULL; + if (vars1.db_tmp) { + free(vars1.db_tmp); + vars1.db_tmp=NULL; } if (ret == true) { - ret = verify_user_attributes(__LINE__, __func__, user); + ret = verify_user_attributes(__LINE__, __func__, vars1.user); } return ret; } diff --git a/lib/MySQL_Session.cpp b/lib/MySQL_Session.cpp index 9d8cfc04b6..88fd6ba4b4 100644 --- a/lib/MySQL_Session.cpp +++ b/lib/MySQL_Session.cpp @@ -5333,7 +5333,8 @@ void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE( proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p , handshake_response=%d , switching_auth_stage=%d , is_encrypted=%d , client_encrypted=%d\n", this, client_myds, handshake_response_return, client_myds->switching_auth_stage, is_encrypted, client_myds->encrypted); if ( - (handshake_response_return == false) && (client_myds->switching_auth_stage == 1) + //(handshake_response_return == false) && (client_myds->switching_auth_stage == 1) + (handshake_response_return == false) && (client_myds->auth_in_progress != 0) ) { l_free(pkt->size,pkt->ptr); proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION,8,"Session=%p , DS=%p . Returning\n", this, client_myds); diff --git a/lib/mysql_data_stream.cpp b/lib/mysql_data_stream.cpp index 35f8786888..0ba545824b 100644 --- a/lib/mysql_data_stream.cpp +++ b/lib/mysql_data_stream.cpp @@ -291,6 +291,7 @@ MySQL_Data_Stream::MySQL_Data_Stream() { encrypted=false; switching_auth_stage = 0; switching_auth_type = 0; + auth_in_progress = 0; x509_subject_alt_name=NULL; ssl=NULL; rbio_ssl = NULL; diff --git a/test/tap/tests/reg_test_3625-sqlite3_session_client_error_limit-t.cpp b/test/tap/tests/reg_test_3625-sqlite3_session_client_error_limit-t.cpp index a56669236d..311b61c929 100644 --- a/test/tap/tests/reg_test_3625-sqlite3_session_client_error_limit-t.cpp +++ b/test/tap/tests/reg_test_3625-sqlite3_session_client_error_limit-t.cpp @@ -208,7 +208,7 @@ int main(int argc, char** argv) { ok( failed_to_connect, - "An invalid user should fail to connect to SQLite3 server, error was: %s", + "A valid user with incorrect password should fail to connect to SQLite3 server, error was: %s", inv_pass_err.c_str() );