Basic implementation of COM_CHANGE_USER client side (issue #187)

pull/248/head
René Cannaò 11 years ago
parent 4b8f5784db
commit 6632ee979f

@ -58,6 +58,9 @@ class MySQL_Protocol {
bool generate_COM_RESET_CONNECTION(bool send, void **ptr, unsigned int *len);
bool generate_COM_CHANGE_USER(bool send, void **ptr, unsigned int *len);
bool generate_pkt_auth_switch_request(bool send, void **ptr, unsigned int *len);
bool process_pkt_auth_swich_response(unsigned char *pkt, unsigned int len);
// bool generate_pkt_column_count(MySQL_Data_Stream *myds, bool send, void **ptr, unsigned int *len, uint8_t sequence_id, uint64_t count);
bool generate_pkt_column_count(bool send, void **ptr, unsigned int *len, uint8_t sequence_id, uint64_t count);
// bool generate_pkt_field(MySQL_Data_Stream *myds, bool send, void **ptr, unsigned int *len, uint8_t sequence_id, char *schema, char *table, char *org_table, char *name, char *org_name, uint16_t charset, uint32_t column_length, uint8_t type, uint16_t flags, uint8_t decimals, bool field_list, uint64_t defvalue_length, char *defvalue);
@ -83,5 +86,6 @@ class MySQL_Protocol {
bool process_pkt_handshake_response(unsigned char *pkt, unsigned int len);
bool process_pkt_initial_handshake(unsigned char *pkt, unsigned int len);
bool process_pkt_COM_QUERY(unsigned char *pkt, unsigned int len);
bool process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned int len);
};
#endif /* __CLASS_MYSQL_PROTOCOL_H */

@ -44,12 +44,17 @@ class MySQL_Session
void handler___status_CONNECTING_SERVER___STATE_NOT_CONNECTED(PtrSize_t *);
void handler___status_CONNECTING_SERVER___STATE_CLIENT_HANDSHAKE(PtrSize_t *, bool *);
void handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(PtrSize_t *, bool *);
void handler___status_CHANGING_USER_CLIENT___STATE_CLIENT_HANDSHAKE(PtrSize_t *, bool *);
void handler___status_CONNECTING_CLIENT___STATE_SSL_INIT(PtrSize_t *);
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_FIELD_LIST(PtrSize_t *);
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_INIT_DB(PtrSize_t *);
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_PING(PtrSize_t *);
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_CHANGE_USER(PtrSize_t *, bool *);
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_PREPARE(PtrSize_t *);
void handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_EXECUTE(PtrSize_t *);
void handler___status_WAITING_SERVER_DATA___STATE_READING_COM_STMT_PREPARE_RESPONSE(PtrSize_t *);

@ -1276,6 +1276,38 @@ bool MySQL_Protocol::generate_COM_CHANGE_USER(bool send, void **ptr, unsigned in
return true;
}
bool MySQL_Protocol::generate_pkt_auth_switch_request(bool send, void **ptr, unsigned int *len) {
proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 7, "Generating auth switch request pkt\n");
mysql_hdr myhdr;
myhdr.pkt_id=1;
myhdr.pkt_length=1 // fe
+ (strlen("mysql_native_password")+1)
+ 20 // scramble
+ 1; // 00
unsigned int size=myhdr.pkt_length+sizeof(mysql_hdr);
unsigned char *_ptr=(unsigned char *)l_alloc0(size);
memcpy(_ptr, &myhdr, sizeof(mysql_hdr));
int l;
l=sizeof(mysql_hdr);
_ptr[l]=0xfe; l++; //0xfe
memcpy(_ptr+l,"mysql_native_password",strlen("mysql_native_password"));
l+=strlen("mysql_native_password");
_ptr[l]=0x00; l++;
memcpy(_ptr+l, (*myds)->myconn->scramble_buff+0, 20); l+=20;
_ptr[l]=0x00; //l+=1; //0x00
if (send==true) {
(*myds)->PSarrayOUT->add((void *)_ptr,size);
(*myds)->DSS=STATE_SERVER_HANDSHAKE;
(*myds)->sess->status=CONNECTING_CLIENT;
}
if (len) { *len=size; }
if (ptr) { *ptr=(void *)_ptr; }
#ifdef DEBUG
if (dump_pkt) { __dump_pkt(__func__,_ptr,size); }
#endif
return true;
}
//bool MySQL_Protocol::generate_pkt_initial_handshake(MySQL_Data_Stream *myds, bool send, void **ptr, unsigned int *len) {
bool MySQL_Protocol::generate_pkt_initial_handshake(bool send, void **ptr, unsigned int *len) {
@ -1532,6 +1564,132 @@ exit_process_pkt_initial_handshake:
}
bool MySQL_Protocol::process_pkt_auth_swich_response(unsigned char *pkt, unsigned int len) {
bool ret=false;
char *password=NULL;
if (len!=sizeof(mysql_hdr)+20) {
return ret;
}
mysql_hdr hdr;
memcpy(&hdr,pkt,sizeof(mysql_hdr));
int default_hostgroup=-1;
bool transaction_persistent;
bool _ret_use_ssl=false;
unsigned char pass[128];
memset(pass,0,128);
pkt+=sizeof(mysql_hdr);
memcpy(pass, pkt, 20);
char reply[SHA_DIGEST_LENGTH+1];
reply[SHA_DIGEST_LENGTH]='\0';
password=GloMyAuth->lookup((char *)userinfo->username, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &transaction_persistent);
if (password==NULL) {
ret=false;
} else {
// if (pass_len==0 && strlen(password)==0) {
// ret=true;
// } else {
proxy_scramble(reply, (*myds)->myconn->scramble_buff, password);
if (memcmp(reply, pass, SHA_DIGEST_LENGTH)==0) {
ret=true;
}
// }
// if (_ret_use_ssl==true) {
// ret=false;
// }
}
return ret;
}
bool MySQL_Protocol::process_pkt_COM_CHANGE_USER(unsigned char *pkt, unsigned int len) {
// FIXME: very buggy function, it doesn't perform any real check
bool ret=false;
int cur=sizeof(mysql_hdr);
unsigned char *user=NULL;
char *password=NULL;
char *db=NULL;
mysql_hdr hdr;
memcpy(&hdr,pkt,sizeof(mysql_hdr));
int default_hostgroup=-1;
bool transaction_persistent;
bool _ret_use_ssl=false;
cur++;
user=pkt+cur;
cur+=strlen((const char *)user);
cur++;
unsigned char pass_len=pkt[cur];
cur++;
unsigned char pass[128];
memset(pass,0,128);
//pkt+=sizeof(mysql_hdr);
memcpy(pass, pkt+cur, pass_len);
char reply[SHA_DIGEST_LENGTH+1];
reply[SHA_DIGEST_LENGTH]='\0';
cur+=pass_len;
db=(char *)pkt+cur;
password=GloMyAuth->lookup((char *)user, USERNAME_FRONTEND, &_ret_use_ssl, &default_hostgroup, &transaction_persistent);
(*myds)->sess->default_hostgroup=default_hostgroup;
(*myds)->sess->transaction_persistent=transaction_persistent;
if (password==NULL) {
ret=false;
} else {
if (pass_len==0 && strlen(password)==0) {
ret=true;
} else {
proxy_scramble(reply, (*myds)->myconn->scramble_buff, password);
if (memcmp(reply, pass, SHA_DIGEST_LENGTH)==0) {
ret=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;
}
}
if (userinfo->username) free(userinfo->username);
if (userinfo->password) free(userinfo->password);
if (ret==true) {
//(*myds)->myconn->options.max_allowed_pkt=max_pkt;
(*myds)->DSS=STATE_CLIENT_HANDSHAKE;
userinfo->username=strdup((const char *)user);
userinfo->password=strdup((const char *)password);
if (db) userinfo->set_schemaname(db,strlen(db));
} else {
// we always duplicate username and password, or crashes happen
userinfo->username=strdup((const char *)user);
/*if (pass_len) */ userinfo->password=strdup((const char *)"");
}
//if (password) free(password);
if (password) l_free_string(password);
/*
//cur+=1;
// g_free(sess->mysql_username);
free(userinfo->username);
//unsigned char *_ptr=pkt+cur;
user=pkt+cur;
//sess->mysql_username=g_strdup(ptr);
cur+=strlen((const char *)user);
cur+=2;
//memcpy(sess->scramble_buf,mypkt->data+cur,20);
memcpy((*myds)->myconn->scramble_buff, pkt+cur, 20);
cur+=20;
//g_free(sess->mysql_schema_cur);
//ptr=mypkt->data+cur;
db=pkt+cur;
//sess->mysql_schema_cur=g_strdup(ptr);
userinfo->username=strdup((const char *)user);
// userinfo->password=strdup((const char *)password);
if (strlen((char *)db)) userinfo->set_schemaname((char *)db,strlen((char *)db)); // FIXME: buggy
proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "CHANGE USER: Username %s , schema %s\n" , userinfo->username, userinfo->schemaname);
*/
// ret=true;
return ret;
}
//bool MySQL_Protocol::process_pkt_handshake_response(MySQL_Data_Stream *myds, unsigned char *pkt, unsigned int len) {
bool MySQL_Protocol::process_pkt_handshake_response(unsigned char *pkt, unsigned int len) {
bool ret=false;

@ -302,7 +302,17 @@ int MySQL_Session::handler() {
//prot.parse_mysql_pkt(&pkt,client_myds);
switch (status) {
/*
case CHANGING_USER_CLIENT:
switch (client_myds->DSS) {
case STATE_CLIENT_HANDSHAKE:
handler___status_CHANGING_USER_CLIENT___STATE_CLIENT_HANDSHAKE(&pkt, &wrong_pass);
break;
default:
assert(0);
}
break;
*/
case CONNECTING_CLIENT:
switch (client_myds->DSS) {
case STATE_SERVER_HANDSHAKE:
@ -365,8 +375,8 @@ int MySQL_Session::handler() {
l_free(pkt.size,pkt.ptr);
}
break;
case _MYSQL_COM_STMT_PREPARE:
handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_PREPARE(&pkt);
case _MYSQL_COM_CHANGE_USER:
handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_CHANGE_USER(&pkt, &wrong_pass);
break;
case _MYSQL_COM_STMT_EXECUTE:
handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_STMT_EXECUTE(&pkt);
@ -1065,6 +1075,28 @@ void MySQL_Session::handler___status_CONNECTING_SERVER___STATE_CLIENT_HANDSHAKE(
void MySQL_Session::handler___status_CHANGING_USER_CLIENT___STATE_CLIENT_HANDSHAKE(PtrSize_t *pkt, bool *wrong_pass) {
// FIXME: no support for SSL yet
if (
client_myds->myprot.process_pkt_auth_swich_response((unsigned char *)pkt->ptr,pkt->size)==true
) {
l_free(pkt->size,pkt->ptr);
client_myds->myprot.generate_pkt_OK(true,NULL,NULL,2,0,0,0,0,NULL);
status=WAITING_CLIENT_DATA;
client_myds->DSS=STATE_SLEEP;
} else {
l_free(pkt->size,pkt->ptr);
proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Wrong credentials for frontend: disconnecting\n");
*wrong_pass=true;
// FIXME: this should become close connection
client_myds->setDSS_STATE_QUERY_SENT_NET();
char *_s=(char *)malloc(strlen(client_myds->myconn->userinfo->username)+100);
sprintf(_s,"Access denied for user '%s' (using password: %s)", client_myds->myconn->userinfo->username, (client_myds->myconn->userinfo->password ? "YES" : "NO"));
client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,2,1045,(char *)"#28000", _s);
free(_s);
}
}
void MySQL_Session::handler___status_CONNECTING_CLIENT___STATE_SERVER_HANDSHAKE(PtrSize_t *pkt, bool *wrong_pass) {
if (
(client_myds->myprot.process_pkt_handshake_response((unsigned char *)pkt->ptr,pkt->size)==true)
@ -1314,6 +1346,35 @@ void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_C
client_myds->DSS=STATE_SLEEP;
}
void MySQL_Session::handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_CHANGE_USER(PtrSize_t *pkt, bool *wrong_pass) {
proxy_debug(PROXY_DEBUG_MYSQL_COM, 5, "Got COM_CHANGE_USER packet\n");
if (admin==false) {
if (client_myds->myprot.process_pkt_COM_CHANGE_USER((unsigned char *)pkt->ptr, pkt->size)==true) {
l_free(pkt->size,pkt->ptr);
//client_myds->myprot.generate_pkt_auth_switch_request(true,NULL,NULL);
//client_myds->DSS=STATE_CLIENT_HANDSHAKE;
//status=CHANGING_USER_CLIENT;
client_myds->myprot.generate_pkt_OK(true,NULL,NULL,1,0,0,0,0,NULL);
client_myds->DSS=STATE_SLEEP;
status=WAITING_CLIENT_DATA;
wrong_pass=false;
} else {
l_free(pkt->size,pkt->ptr);
proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Wrong credentials for frontend: disconnecting\n");
*wrong_pass=true;
// FIXME: this should become close connection
client_myds->setDSS_STATE_QUERY_SENT_NET();
char *_s=(char *)malloc(strlen(client_myds->myconn->userinfo->username)+100);
sprintf(_s,"Access denied for user '%s' (using password: %s)", client_myds->myconn->userinfo->username, (client_myds->myconn->userinfo->password ? "YES" : "NO"));
client_myds->myprot.generate_pkt_ERR(true,NULL,NULL,2,1045,(char *)"#28000", _s);
free(_s);
}
} else {
//FIXME: send an error message saying "not supported" or disconnect
l_free(pkt->size,pkt->ptr);
}
}
void MySQL_Session::handler___client_DSS_QUERY_SENT___server_DSS_NOT_INITIALIZED__get_connection() {
// Get a MySQL Connection

Loading…
Cancel
Save