Authentication funcs changed:
support for user without password. Some better documentation in gw_mysql_connect.
This commit is contained in:
@ -29,7 +29,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static char *version_str = "V1.0.0";
|
static char *version_str = "V1.0.0";
|
||||||
|
extern char *gw_strend(register const char *s);
|
||||||
|
int gw_mysql_connect(char *host, int port, char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn);
|
||||||
int gw_create_backend_connection(DCB *client_dcb, SERVER *server, SESSION *in_session);
|
int gw_create_backend_connection(DCB *client_dcb, SERVER *server, SESSION *in_session);
|
||||||
int gw_read_backend_event(DCB* dcb);
|
int gw_read_backend_event(DCB* dcb);
|
||||||
int gw_write_backend_event(DCB *dcb);
|
int gw_write_backend_event(DCB *dcb);
|
||||||
@ -305,20 +306,18 @@ int gw_create_backend_connection(DCB *backend, SERVER *server, SESSION *session)
|
|||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "<<<< NOT Connected to backend mysql server!!!\n");
|
fprintf(stderr, "<<<< NOT Connected to backend mysql server!!!\n");
|
||||||
backend->fd = -1;
|
backend->fd = -1;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if connected, it will be addeed to the epoll from the caller of connect()
|
// if connected, it will be addeed to the epoll from the caller of connect()
|
||||||
|
|
||||||
if (backend->fd <= 0) {
|
if (backend->fd <= 0) {
|
||||||
perror("ERROR: epoll_ctl: backend sock");
|
perror("ERROR: epoll_ctl: backend sock");
|
||||||
return 1;
|
backend->fd = -1;
|
||||||
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "--> Backend conn added, bk_fd [%i], scramble [%s], is session with client_fd [%i]\n", backend->fd, ptr_proto->scramble, session->client->fd);
|
fprintf(stderr, "--> Backend conn added, bk_fd [%i], scramble [%s], is session with client_fd [%i]\n", backend->fd, ptr_proto->scramble, session->client->fd);
|
||||||
backend->state = DCB_STATE_POLLING;
|
backend->state = DCB_STATE_POLLING;
|
||||||
//(backend->func).read = gw_read_backend_event;
|
|
||||||
//(backend->func).write = gw_MySQLWrite_backend;
|
|
||||||
//(backend->func).write_ready = gw_write_backend_event;
|
|
||||||
//(backend->func).error = gw_error_backend_event;
|
|
||||||
|
|
||||||
return backend->fd;
|
return backend->fd;
|
||||||
}
|
}
|
||||||
@ -330,3 +329,440 @@ gw_backend_close(DCB *dcb)
|
|||||||
{
|
{
|
||||||
dcb_close(dcb);
|
dcb_close(dcb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new MySQL connection.
|
||||||
|
*
|
||||||
|
* This routine performs the full MySQL connection to the specified server.
|
||||||
|
* It does
|
||||||
|
* - socket init
|
||||||
|
* - socket connect
|
||||||
|
* - server handshake parsing
|
||||||
|
* - authenticatio reply
|
||||||
|
* - the Auth ack receive
|
||||||
|
*
|
||||||
|
* Please note, all socket operation are in blocking state
|
||||||
|
* Status: work in progress.
|
||||||
|
*
|
||||||
|
* @param host The TCP/IP host address to connect to
|
||||||
|
* @param port The TCP/IP host port to connect to
|
||||||
|
* @param dbname The optional database name. Use NULL if not interested in
|
||||||
|
* @param user The MySQL database Username: required
|
||||||
|
* @param passwd The MySQL database Password: required
|
||||||
|
* @param conn The MySQLProtocol structure to be filled: must be preallocated with gw_mysql_init()
|
||||||
|
* @return 0 on Success or 1 on Failure.
|
||||||
|
*/
|
||||||
|
int gw_mysql_connect(char *host, int port, char *dbname, char *user, uint8_t *passwd, MySQLProtocol *conn) {
|
||||||
|
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
int compress = 0;
|
||||||
|
int rv;
|
||||||
|
int so = 0;
|
||||||
|
int ciclo = 0;
|
||||||
|
char buffer[SMALL_CHUNK];
|
||||||
|
uint8_t packet_buffer[SMALL_CHUNK];
|
||||||
|
uint8_t *payload = NULL;
|
||||||
|
int server_protocol;
|
||||||
|
uint8_t *server_version_end = NULL;
|
||||||
|
uint16_t mysql_server_capabilities_one;
|
||||||
|
uint16_t mysql_server_capabilities_two;
|
||||||
|
unsigned long tid =0;
|
||||||
|
long bytes;
|
||||||
|
uint8_t scramble_data_1[8 + 1] = "";
|
||||||
|
uint8_t scramble_data_2[12 + 1] = "";
|
||||||
|
uint8_t capab_ptr[4];
|
||||||
|
int scramble_len;
|
||||||
|
uint8_t scramble[GW_MYSQL_SCRAMBLE_SIZE + 1];
|
||||||
|
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE + 1];
|
||||||
|
uint8_t client_capabilities[4];
|
||||||
|
uint32_t server_capabilities;
|
||||||
|
uint32_t final_capabilities;
|
||||||
|
char dbpass[500]="";
|
||||||
|
|
||||||
|
char *curr_db = NULL;
|
||||||
|
uint8_t *curr_passwd = NULL;
|
||||||
|
|
||||||
|
if (strlen(dbname))
|
||||||
|
curr_db = dbname;
|
||||||
|
|
||||||
|
if (strlen(passwd))
|
||||||
|
curr_passwd = passwd;
|
||||||
|
|
||||||
|
conn->state = MYSQL_ALLOC;
|
||||||
|
conn->fd = -1;
|
||||||
|
|
||||||
|
//#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, ")))) Connect to MySQL: user[%s], SHA1(passwd)[%s], db [%s]\n", user, passwd, dbname);
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
memset(&serv_addr, 0, sizeof serv_addr);
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
|
||||||
|
so = socket(AF_INET,SOCK_STREAM,0);
|
||||||
|
if (so < 0) {
|
||||||
|
fprintf(stderr, "Errore creazione socket: [%s] %i\n", strerror(errno), errno);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->fd = so;
|
||||||
|
|
||||||
|
setipaddress(&serv_addr.sin_addr, host);
|
||||||
|
serv_addr.sin_port = htons(port);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Socket initialized\n");
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
if ((rv = connect(so, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) < 0) {
|
||||||
|
fprintf(stderr, "Errore connect %i, %s: RV = [%i]\n", errno, strerror(errno), rv);
|
||||||
|
|
||||||
|
if (errno == EINPROGRESS) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
close(so);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "CONNECT is DONE\n");
|
||||||
|
fprintf(stderr, "Socket FD is %i\n", so);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
memset(&buffer, '\0', sizeof(buffer));
|
||||||
|
|
||||||
|
bytes = 16384;
|
||||||
|
|
||||||
|
rv = read(so, buffer, bytes);
|
||||||
|
|
||||||
|
if ( rv >0 ) {
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "RESPONSE ciclo %i HO letto [%s] bytes %li\n",ciclo, buffer, bytes);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
ciclo++;
|
||||||
|
} else {
|
||||||
|
if (rv == 0 && errno == EOF) {
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "EOF reached. Bytes = %li\n", bytes);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "###### Receive error FINAL : connection not completed %i %s: RV = [%i]\n", errno, strerror(errno), rv);
|
||||||
|
#endif
|
||||||
|
close(so);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fwrite(buffer, bytes, 1, stderr);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//decode mysql handshake
|
||||||
|
|
||||||
|
payload = buffer + 4;
|
||||||
|
server_protocol= payload[0];
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Server Protocol [%i]\n", server_protocol);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
payload++;
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Protocol Version [%s]\n", payload);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
server_version_end = (uint8_t *) gw_strend((char*) payload);
|
||||||
|
payload = server_version_end + 1;
|
||||||
|
|
||||||
|
// TID
|
||||||
|
tid = gw_mysql_get_byte4(payload);
|
||||||
|
memcpy(&conn->tid, &tid, 4);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Thread ID is %lu\n", conn->tid);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
payload +=4;
|
||||||
|
|
||||||
|
// scramble_part 1
|
||||||
|
memcpy(scramble_data_1, payload, 8);
|
||||||
|
payload += 8;
|
||||||
|
|
||||||
|
// 1 filler
|
||||||
|
payload++;
|
||||||
|
|
||||||
|
mysql_server_capabilities_one = gw_mysql_get_byte2(payload);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Capab_1[\n");
|
||||||
|
fwrite(&mysql_server_capabilities_one, 2, 1, stderr);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//2 capab_part 1 + 1 language + 2 server_status
|
||||||
|
payload +=5;
|
||||||
|
|
||||||
|
mysql_server_capabilities_two = gw_mysql_get_byte2(payload);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "]Capab_2[\n");
|
||||||
|
fwrite(&mysql_server_capabilities_two, 2, 1, stderr);
|
||||||
|
fprintf(stderr, "]\n");
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memcpy(&capab_ptr, &mysql_server_capabilities_one, 2);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Capab_1[\n");
|
||||||
|
fwrite(capab_ptr, 2, 1, stderr);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memcpy(&(capab_ptr[2]), &mysql_server_capabilities_two, 2);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Capab_2[\n");
|
||||||
|
fwrite(capab_ptr, 2, 1, stderr);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 2 capab_part 2
|
||||||
|
payload+=2;
|
||||||
|
|
||||||
|
scramble_len = payload[0] -1;
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Scramble_len [%i]\n", scramble_len);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
payload += 11;
|
||||||
|
|
||||||
|
memcpy(scramble_data_2, payload, scramble_len - 8);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Scramble_buff1[");
|
||||||
|
fwrite(scramble_data_1, 8, 1, stderr);
|
||||||
|
fprintf(stderr, "]\nScramble_buff2 [");
|
||||||
|
fwrite(scramble_data_2, scramble_len - 8, 1, stderr);
|
||||||
|
fprintf(stderr, "]\n");
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memcpy(scramble, scramble_data_1, 8);
|
||||||
|
memcpy(scramble + 8, scramble_data_2, scramble_len - 8);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Full Scramble 20 bytes is [\n");
|
||||||
|
fwrite(scramble, GW_MYSQL_SCRAMBLE_SIZE, 1, stderr);
|
||||||
|
fprintf(stderr, "\n]\n");
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memcpy(conn->scramble, scramble, GW_MYSQL_SCRAMBLE_SIZE);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Scramble from MYSQL_Conn is [\n");
|
||||||
|
fwrite(scramble, GW_MYSQL_SCRAMBLE_SIZE, 1, stderr);
|
||||||
|
fprintf(stderr, "\n]\n");
|
||||||
|
fflush(stderr);
|
||||||
|
fprintf(stderr, "Now sending user, pass & db\n[");
|
||||||
|
fwrite(&server_capabilities, 4, 1, stderr);
|
||||||
|
fprintf(stderr, "]\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "CAPABS [%u]\n", final_capabilities);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
memset(packet_buffer, '\0', sizeof(packet_buffer));
|
||||||
|
//packet_header(byte3 +1 pack#)
|
||||||
|
packet_buffer[3] = '\x01';
|
||||||
|
|
||||||
|
final_capabilities |= GW_MYSQL_CAPABILITIES_PROTOCOL_41;
|
||||||
|
final_capabilities |= GW_MYSQL_CAPABILITIES_CLIENT;
|
||||||
|
if (compress) {
|
||||||
|
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
|
||||||
|
fprintf(stderr, "Backend Connection with compression\n");
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curr_passwd != NULL) {
|
||||||
|
uint8_t hash1[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||||
|
uint8_t hash2[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||||
|
uint8_t new_sha[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||||
|
|
||||||
|
|
||||||
|
memcpy(hash1, passwd, GW_MYSQL_SCRAMBLE_SIZE);
|
||||||
|
gw_sha1_str(hash1, GW_MYSQL_SCRAMBLE_SIZE, hash2);
|
||||||
|
gw_bin2hex(dbpass, hash2, GW_MYSQL_SCRAMBLE_SIZE);
|
||||||
|
gw_sha1_2_str(scramble, GW_MYSQL_SCRAMBLE_SIZE, hash2, GW_MYSQL_SCRAMBLE_SIZE, new_sha);
|
||||||
|
gw_str_xor(client_scramble, new_sha, hash1, GW_MYSQL_SCRAMBLE_SIZE);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Hash1 [%s]\n", hash1);
|
||||||
|
fprintf(stderr, "Hash2 [%s]\n", hash2);
|
||||||
|
fprintf(stderr, "SHA1(SHA1(password in hex)\n");
|
||||||
|
fprintf(stderr, "PAss [%s]\n", dbpass);
|
||||||
|
fflush(stderr);
|
||||||
|
fprintf(stderr, "newsha [%s]\n", new_sha);
|
||||||
|
fprintf(stderr, "Client send scramble 20 [\n");
|
||||||
|
fwrite(client_scramble, GW_MYSQL_SCRAMBLE_SIZE, 1, stderr);
|
||||||
|
fprintf(stderr, "\n]\n");
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curr_db == NULL) {
|
||||||
|
// now without db!!
|
||||||
|
final_capabilities &= ~GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB;
|
||||||
|
} else {
|
||||||
|
final_capabilities |= GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = packet_buffer + 4;
|
||||||
|
|
||||||
|
final_capabilities |= GW_MYSQL_CAPABILITIES_PLUGIN_AUTH;
|
||||||
|
|
||||||
|
gw_mysql_set_byte4(client_capabilities, final_capabilities);
|
||||||
|
memcpy(payload, client_capabilities, 4);
|
||||||
|
|
||||||
|
//packet_buffer[4] = '\x8d';
|
||||||
|
//packet_buffer[5] = '\xa6';
|
||||||
|
//packet_buffer[6] = '\x0f';
|
||||||
|
//packet_buffer[7] = '\x00';
|
||||||
|
|
||||||
|
// set now the max-packet size
|
||||||
|
payload += 4;
|
||||||
|
gw_mysql_set_byte4(payload, 16777216);
|
||||||
|
|
||||||
|
// set the charset
|
||||||
|
payload += 4;
|
||||||
|
*payload = '\x08';
|
||||||
|
|
||||||
|
payload++;
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "User is [%s]\n", user);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// 4 + 4 + 4 + 1 + 23 = 36
|
||||||
|
payload += 23;
|
||||||
|
strcpy(payload, user);
|
||||||
|
|
||||||
|
// 4 + 4 + 1 + 23 = 32 + 1 (scramble_len) + 20 (fixed_scramble) + 1 (user NULL term) + 1 (db NULL term) = 55
|
||||||
|
bytes = 32;
|
||||||
|
|
||||||
|
bytes += strlen(user);
|
||||||
|
// the NULL
|
||||||
|
bytes++;
|
||||||
|
|
||||||
|
payload += strlen(user);
|
||||||
|
payload++;
|
||||||
|
|
||||||
|
if (curr_passwd != NULL) {
|
||||||
|
// set the auth-length
|
||||||
|
*payload = GW_MYSQL_SCRAMBLE_SIZE;
|
||||||
|
payload++;
|
||||||
|
bytes++;
|
||||||
|
|
||||||
|
//copy the 20 bytes scramble data after packet_buffer+36+user+NULL+1 (byte of auth-length)
|
||||||
|
memcpy(payload, client_scramble, GW_MYSQL_SCRAMBLE_SIZE);
|
||||||
|
|
||||||
|
payload += GW_MYSQL_SCRAMBLE_SIZE;
|
||||||
|
bytes += GW_MYSQL_SCRAMBLE_SIZE;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// skip the auth-length and write a NULL
|
||||||
|
payload++;
|
||||||
|
bytes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the db is not NULL append it
|
||||||
|
if (curr_db) {
|
||||||
|
strcpy(payload, curr_db);
|
||||||
|
payload += strlen(curr_db);
|
||||||
|
payload++;
|
||||||
|
bytes += strlen(curr_db);
|
||||||
|
// the NULL
|
||||||
|
bytes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(payload, "mysql_native_password");
|
||||||
|
|
||||||
|
payload += strlen("mysql_native_password");
|
||||||
|
payload++;
|
||||||
|
|
||||||
|
bytes +=strlen("mysql_native_password");
|
||||||
|
bytes++;
|
||||||
|
|
||||||
|
gw_mysql_set_byte3(packet_buffer, bytes);
|
||||||
|
|
||||||
|
// the packet header
|
||||||
|
bytes += 4;
|
||||||
|
|
||||||
|
rv = write(so, packet_buffer, bytes);
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "Sent [%s], [%i] bytes\n", packet_buffer, bytes);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (rv == -1) {
|
||||||
|
fprintf(stderr, "CONNECT Error in send auth\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 4096;
|
||||||
|
|
||||||
|
memset(buffer, '\0', sizeof (buffer));
|
||||||
|
|
||||||
|
rv = read(so, buffer, 4096);
|
||||||
|
|
||||||
|
|
||||||
|
if (rv == -1) {
|
||||||
|
fprintf(stderr, "CONNCET Error in recv OK for auth\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "ok packet\[");
|
||||||
|
fwrite(buffer, bytes, 1, stderr);
|
||||||
|
fprintf(stderr, "]\n");
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
if (buffer[4] == '\x00') {
|
||||||
|
#ifdef MYSQL_CONN_DEBUG
|
||||||
|
fprintf(stderr, "OK packet received, packet # %i\n", buffer[3]);
|
||||||
|
fflush(stderr);
|
||||||
|
#endif
|
||||||
|
conn->state = MYSQL_IDLE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
close(so);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -179,6 +179,89 @@ mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mys
|
|||||||
return sizeof(mysql_packet_header) + mysql_payload_size;
|
return sizeof(mysql_packet_header) + mysql_payload_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mysql_send_auth_error
|
||||||
|
*
|
||||||
|
* Send a MySQL protocol Generic ERR message, to the dcb
|
||||||
|
* Note the errno and state are still fixed now
|
||||||
|
*
|
||||||
|
* @param dcb Descriptor Control Block for the connection to which the OK is sent
|
||||||
|
* @param packet_number
|
||||||
|
* @param in_affected_rows
|
||||||
|
* @param mysql_message
|
||||||
|
* @return packet length
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
mysql_send_custom_error (DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message) {
|
||||||
|
uint8_t *outbuf = NULL;
|
||||||
|
uint8_t mysql_payload_size = 0;
|
||||||
|
uint8_t mysql_packet_header[4];
|
||||||
|
uint8_t *mysql_payload = NULL;
|
||||||
|
uint8_t field_count = 0;
|
||||||
|
uint8_t affected_rows = 0;
|
||||||
|
uint8_t insert_id = 0;
|
||||||
|
uint8_t mysql_err[2];
|
||||||
|
uint8_t mysql_statemsg[6];
|
||||||
|
unsigned int mysql_errno = 0;
|
||||||
|
const char *mysql_error_msg = NULL;
|
||||||
|
const char *mysql_state = NULL;
|
||||||
|
|
||||||
|
GWBUF *buf;
|
||||||
|
|
||||||
|
mysql_errno = 2003;
|
||||||
|
mysql_error_msg = "An errorr occurred ...";
|
||||||
|
mysql_state = "HY000";
|
||||||
|
|
||||||
|
field_count = 0xff;
|
||||||
|
gw_mysql_set_byte2(mysql_err, mysql_errno);
|
||||||
|
mysql_statemsg[0]='#';
|
||||||
|
memcpy(mysql_statemsg+1, mysql_state, 5);
|
||||||
|
|
||||||
|
if (mysql_message != NULL) {
|
||||||
|
mysql_error_msg = mysql_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_payload_size = sizeof(field_count) + sizeof(mysql_err) + sizeof(mysql_statemsg) + strlen(mysql_error_msg);
|
||||||
|
|
||||||
|
// allocate memory for packet header + payload
|
||||||
|
if ((buf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size)) == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
outbuf = GWBUF_DATA(buf);
|
||||||
|
|
||||||
|
// write packet header with packet number
|
||||||
|
gw_mysql_set_byte3(mysql_packet_header, mysql_payload_size);
|
||||||
|
mysql_packet_header[3] = packet_number;
|
||||||
|
|
||||||
|
// write header
|
||||||
|
memcpy(outbuf, mysql_packet_header, sizeof(mysql_packet_header));
|
||||||
|
|
||||||
|
mysql_payload = outbuf + sizeof(mysql_packet_header);
|
||||||
|
|
||||||
|
// write field
|
||||||
|
memcpy(mysql_payload, &field_count, sizeof(field_count));
|
||||||
|
mysql_payload = mysql_payload + sizeof(field_count);
|
||||||
|
|
||||||
|
// write errno
|
||||||
|
memcpy(mysql_payload, mysql_err, sizeof(mysql_err));
|
||||||
|
mysql_payload = mysql_payload + sizeof(mysql_err);
|
||||||
|
|
||||||
|
// write sqlstate
|
||||||
|
memcpy(mysql_payload, mysql_statemsg, sizeof(mysql_statemsg));
|
||||||
|
mysql_payload = mysql_payload + sizeof(mysql_statemsg);
|
||||||
|
|
||||||
|
// write err messg
|
||||||
|
memcpy(mysql_payload, mysql_error_msg, strlen(mysql_error_msg));
|
||||||
|
|
||||||
|
// writing data in the Client buffer queue
|
||||||
|
dcb->func.write(dcb, buf);
|
||||||
|
|
||||||
|
return sizeof(mysql_packet_header) + mysql_payload_size;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mysql_send_auth_error
|
* mysql_send_auth_error
|
||||||
*
|
*
|
||||||
@ -219,9 +302,6 @@ mysql_send_auth_error (DCB *dcb, int packet_number, int in_affected_rows, const
|
|||||||
|
|
||||||
if (mysql_message != NULL) {
|
if (mysql_message != NULL) {
|
||||||
mysql_error_msg = mysql_message;
|
mysql_error_msg = mysql_message;
|
||||||
} else {
|
|
||||||
mysql_error_msg = "Access denied!";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mysql_payload_size = sizeof(field_count) + sizeof(mysql_err) + sizeof(mysql_statemsg) + strlen(mysql_error_msg);
|
mysql_payload_size = sizeof(field_count) + sizeof(mysql_err) + sizeof(mysql_statemsg) + strlen(mysql_error_msg);
|
||||||
@ -450,36 +530,42 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
|||||||
|
|
||||||
stage1_hash = client_data->client_sha1;
|
stage1_hash = client_data->client_sha1;
|
||||||
username = client_data->user;
|
username = client_data->user;
|
||||||
database = client_data->db;
|
|
||||||
|
|
||||||
memcpy(&protocol->client_capabilities, client_auth_packet + 4, 4);
|
memcpy(&protocol->client_capabilities, client_auth_packet + 4, 4);
|
||||||
|
|
||||||
connect_with_db = GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB & gw_mysql_get_byte4(&protocol->client_capabilities);
|
connect_with_db = GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB & gw_mysql_get_byte4(&protocol->client_capabilities);
|
||||||
compress = GW_MYSQL_CAPABILITIES_COMPRESS & gw_mysql_get_byte4(&protocol->client_capabilities);
|
compress = GW_MYSQL_CAPABILITIES_COMPRESS & gw_mysql_get_byte4(&protocol->client_capabilities);
|
||||||
strncpy(username, client_auth_packet + 4 + 4 + 4 + 1 + 23, 128);
|
|
||||||
|
|
||||||
|
// now get the user
|
||||||
|
strncpy(username, client_auth_packet + 4 + 4 + 4 + 1 + 23, 128);
|
||||||
|
fprintf(stderr, "<<< Client username is [%s]\n", username);
|
||||||
|
|
||||||
|
// get the auth token len
|
||||||
memcpy(&auth_token_len, client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1, 1);
|
memcpy(&auth_token_len, client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1, 1);
|
||||||
|
|
||||||
auth_token = (uint8_t *)malloc(auth_token_len);
|
|
||||||
memcpy(auth_token, client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1 + 1, auth_token_len);
|
|
||||||
|
|
||||||
if (connect_with_db) {
|
if (connect_with_db) {
|
||||||
fprintf(stderr, "<<< Client is connected with db\n");
|
database = client_data->db;
|
||||||
|
strncpy(database, client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1 + 1 + auth_token_len, 128);
|
||||||
|
fprintf(stderr, "<<< Client selected db is [%s]\n", database);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "<<< Client is NOT connected with db\n");
|
fprintf(stderr, "<<< Client is NOT connected with db\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connect_with_db) {
|
fprintf(stderr, "HERE auth token len is %i\n", auth_token_len);
|
||||||
strncpy(database, client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1 + 1 + auth_token_len, 128);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "<<< Client selected db is [%s]\n", database);
|
|
||||||
|
|
||||||
fprintf(stderr, "<<< Client username is [%s]\n", username);
|
// allocate memory for token only if auth_token_len > 0
|
||||||
|
if (auth_token_len) {
|
||||||
|
auth_token = (uint8_t *)malloc(auth_token_len);
|
||||||
|
memcpy(auth_token, client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1 + 1, auth_token_len);
|
||||||
|
}
|
||||||
|
|
||||||
// decode the token and check the password
|
// decode the token and check the password
|
||||||
|
// Note: if auth_token_len == 0 && auth_token == NULL, user is without password so check only the user.
|
||||||
auth_ret = gw_check_mysql_scramble_data(dcb, auth_token, auth_token_len, protocol->scramble, sizeof(protocol->scramble), username, stage1_hash);
|
auth_ret = gw_check_mysql_scramble_data(dcb, auth_token, auth_token_len, protocol->scramble, sizeof(protocol->scramble), username, stage1_hash);
|
||||||
|
|
||||||
free(auth_token);
|
// let's free the auth_token now
|
||||||
|
if (auth_token)
|
||||||
|
free(auth_token);
|
||||||
|
|
||||||
if (auth_ret == 0) {
|
if (auth_ret == 0) {
|
||||||
fprintf(stderr, "<<< CLIENT AUTH is OK\n");
|
fprintf(stderr, "<<< CLIENT AUTH is OK\n");
|
||||||
@ -509,14 +595,15 @@ static int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_pas
|
|||||||
fprintf(stderr, ">>> MYSQL user NOT FOUND: %s\n", username);
|
fprintf(stderr, ">>> MYSQL user NOT FOUND: %s\n", username);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
fprintf(stderr, ">>> MYSQL user FOUND !!!!: %si:%s\n", username, user_password);
|
fprintf(stderr, ">>> MYSQL user FOUND !!!!: [%s]:[%s]\n", username, user_password);
|
||||||
|
|
||||||
// convert hex data (40 bytes) to binary (20 bytes)
|
// convert hex data (40 bytes) to binary (20 bytes)
|
||||||
// gateway_password represents the SHA1(SHA1(real_password))
|
// gateway_password represents the SHA1(SHA1(real_password))
|
||||||
// please not real_password is unknown and SHA1(real_password)
|
// please not real_password is unknown and SHA1(real_password)
|
||||||
// is unknown as well
|
// is unknown as well
|
||||||
|
|
||||||
gw_hex2bin(gateway_password, user_password, SHA_DIGEST_LENGTH * 2);
|
if (strlen(user_password))
|
||||||
|
gw_hex2bin(gateway_password, user_password, SHA_DIGEST_LENGTH * 2);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -529,24 +616,30 @@ static int gw_check_mysql_scramble_data(DCB *dcb, uint8_t *token, unsigned int t
|
|||||||
uint8_t password[GW_MYSQL_SCRAMBLE_SIZE]="";
|
uint8_t password[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||||
int ret_val = 1;
|
int ret_val = 1;
|
||||||
|
|
||||||
if ((username == NULL) || (token == NULL) || (scramble == NULL) || (stage1_hash == NULL)) {
|
if ((username == NULL) || (scramble == NULL) || (stage1_hash == NULL)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the user's password from repository in SHA1(SHA1(real_password));
|
// get the user's password from repository in SHA1(SHA1(real_password));
|
||||||
// please note 'real_password' in unknown!
|
// please note 'real_password' is unknown!
|
||||||
ret_val = gw_find_mysql_user_password_sha1(username, password, (DCB *) dcb);
|
ret_val = gw_find_mysql_user_password_sha1(username, password, (DCB *) dcb);
|
||||||
|
|
||||||
if (ret_val) {
|
if (ret_val) {
|
||||||
//fprintf(stderr, "<<<< User [%s] was not found\n", username);
|
fprintf(stderr, "<<<< User [%s] was not found\n", username);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
//fprintf(stderr, "<<<< User [%s] OK\n", username);
|
fprintf(stderr, "<<<< User [%s] OK\n", username);
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert in hex format: this is the content of mysql.user table, field password without the '*' prefix
|
if (token && token_len) {
|
||||||
// an it is 40 bytes long
|
fprintf(stderr, ">>> continue with auth\n");
|
||||||
gw_bin2hex(hex_double_sha1, password, SHA_DIGEST_LENGTH);
|
// convert in hex format: this is the content of mysql.user table, field password without the '*' prefix
|
||||||
|
// an it is 40 bytes long
|
||||||
|
gw_bin2hex(hex_double_sha1, password, SHA_DIGEST_LENGTH);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, ">>> continue WITHOUT auth, no password\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////
|
///////////////////////////
|
||||||
// Auth check in 3 steps
|
// Auth check in 3 steps
|
||||||
@ -584,6 +677,9 @@ static int gw_check_mysql_scramble_data(DCB *dcb, uint8_t *token, unsigned int t
|
|||||||
|
|
||||||
gw_sha1_str(step2, SHA_DIGEST_LENGTH, check_hash);
|
gw_sha1_str(step2, SHA_DIGEST_LENGTH, check_hash);
|
||||||
|
|
||||||
|
|
||||||
|
fprintf(stderr, "<<<< Client_SHA1 [%20s]\n", stage1_hash);
|
||||||
|
|
||||||
#ifdef GW_DEBUG_CLIENT_AUTH
|
#ifdef GW_DEBUG_CLIENT_AUTH
|
||||||
{
|
{
|
||||||
char inpass[128]="";
|
char inpass[128]="";
|
||||||
@ -786,6 +882,21 @@ int gw_read_client_event(DCB* dcb) {
|
|||||||
//fprintf(stderr, "<<< Reading from Client %i bytes: [%s]\n", len, ptr_buff);
|
//fprintf(stderr, "<<< Reading from Client %i bytes: [%s]\n", len, ptr_buff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!rsession) {
|
||||||
|
|
||||||
|
if (mysql_command == '\x01') {
|
||||||
|
fprintf(stderr, "COM_QUIT received with no connected backends\n");
|
||||||
|
(dcb->func).close(dcb);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
mysql_send_custom_error(dcb, 1, 0, "Connection to backend lost");
|
||||||
|
protocol->state = MYSQL_IDLE;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////
|
///////////////////////////
|
||||||
// Handling the COM_QUIT
|
// Handling the COM_QUIT
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
@ -870,7 +981,7 @@ int gw_write_client_event(DCB *dcb) {
|
|||||||
// still to implement
|
// still to implement
|
||||||
mysql_send_auth_error(dcb, 2, 0, "Authorization failed");
|
mysql_send_auth_error(dcb, 2, 0, "Authorization failed");
|
||||||
|
|
||||||
dcb->func.error(dcb);
|
dcb->func.close(dcb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -967,8 +1078,6 @@ int gw_MySQLListener(DCB *listener, char *config_bind) {
|
|||||||
|
|
||||||
listener->state = DCB_STATE_LISTENING;
|
listener->state = DCB_STATE_LISTENING;
|
||||||
|
|
||||||
fprintf(stderr, "HERE listening\n");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1060,46 +1169,9 @@ static int gw_error_client_event(DCB *dcb) {
|
|||||||
|
|
||||||
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||||
|
|
||||||
fprintf(stderr, "#### Handle error function for [%i] is [%s]\n", dcb->state, gw_dcb_state2string(dcb->state));
|
fprintf(stderr, "#### Handle error function gw_error_client_event, for [%i] is [%s]\n", dcb->state, gw_dcb_state2string(dcb->state));
|
||||||
|
|
||||||
if (dcb->state == DCB_STATE_DISCONNECTED) {
|
|
||||||
fprintf(stderr, "#### Handle error function, session is %p\n", dcb->session);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef GW_EVENT_DEBUG
|
|
||||||
if (event != -1) {
|
|
||||||
fprintf(stderr, ">>>>>> DCB state %i, Protocol State %i: event %i, %i\n", dcb->state, protocol->state, event & EPOLLERR, event & EPOLLHUP);
|
|
||||||
if(event & EPOLLHUP)
|
|
||||||
fprintf(stderr, "EPOLLHUP\n");
|
|
||||||
|
|
||||||
if(event & EPOLLERR)
|
|
||||||
fprintf(stderr, "EPOLLERR\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (dcb->state != DCB_STATE_LISTENING) {
|
|
||||||
if (poll_remove_dcb(dcb) == -1) {
|
|
||||||
fprintf(stderr, "***** poll_remove_dcb: from events check failed to delete %i, [%i]:[%s]\n", dcb->fd, errno, strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef GW_EVENT_DEBUG
|
|
||||||
fprintf(stderr, "closing fd [%i]=[%i], from events\n", dcb->fd, protocol->fd);
|
|
||||||
#endif
|
|
||||||
if (dcb->fd) {
|
|
||||||
|
|
||||||
gw_mysql_close((MySQLProtocol **)&dcb->protocol);
|
|
||||||
|
|
||||||
dcb->state = DCB_STATE_DISCONNECTED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//free(dcb->session);
|
|
||||||
dcb->state = DCB_STATE_FREED;
|
|
||||||
|
|
||||||
fprintf(stderr, "#### Handle error function RETURN for [%i] is [%s]\n", dcb->state, gw_dcb_state2string(dcb->state));
|
fprintf(stderr, "#### Handle error function RETURN for [%i] is [%s]\n", dcb->state, gw_dcb_state2string(dcb->state));
|
||||||
//free(dcb);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user