mysql_client.c, added functions create_auth_fail_str, and get_username_from_auth to implement creation of similar error msg with MySQL. There is one difference left, because MaxScale prints IP address instead of hostname in error message.
This commit is contained in:
VilhoRaatikka
2014-10-07 18:00:22 +03:00
parent 54b6271c55
commit 07fc5162a0

View File

@ -68,6 +68,8 @@ int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char*
int MySQLSendHandshake(DCB* dcb); int MySQLSendHandshake(DCB* dcb);
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue); static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
static int route_by_statement(SESSION *, GWBUF **); static int route_by_statement(SESSION *, GWBUF **);
static char* create_auth_fail_str(GWBUF* readbuf, char* hostaddr, char* sha1);
static char* get_username_from_auth(char* ptr, uint8_t* data);
/* /*
* The "module object" for the mysqld client protocol module. * The "module object" for the mysqld client protocol module.
@ -372,9 +374,9 @@ MySQLSendHandshake(DCB* dcb)
* The useful data: user, db, client_sha1 are copied into the MYSQL_session * dcb->session->data * The useful data: user, db, client_sha1 are copied into the MYSQL_session * dcb->session->data
* client_capabilitiesa are copied into the dcb->protocol * client_capabilitiesa are copied into the dcb->protocol
* *
* @param dcb Descriptor Control Block of the client * @param dcb Descriptor Control Block of the client
* @param queue The GWBUF with data from client * @param queue The GWBUF with data from client
* @return 0 for Authentication ok, !=0 for failed autht * @return 0 If succeed, otherwise non-zero value
* *
*/ */
@ -432,12 +434,10 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
&protocol->client_capabilities); &protocol->client_capabilities);
*/ */
/* now get the user */ username = get_username_from_auth(username, client_auth_packet);
strncpy(username, (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23), MYSQL_USER_MAXLEN);
if (username == NULL)
{
/* the empty username field is not allowed */
if (!strlen(username)) {
return 1; return 1;
} }
@ -448,9 +448,9 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
if (connect_with_db) { if (connect_with_db) {
database = client_data->db; database = client_data->db;
strncpy(database, strncpy(database,
(char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + (char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) +
1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN); 1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN);
} }
/* allocate memory for token only if auth_token_len > 0 */ /* allocate memory for token only if auth_token_len > 0 */
@ -489,10 +489,88 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
{ {
dcb->user = strdup(client_data->user); dcb->user = strdup(client_data->user);
} }
return auth_ret; return auth_ret;
} }
/**
* Read username from MySQL authentication packet.
*
* @param ptr address where to write the result or NULL if memory
* is allocated here.
* @param data Address of MySQL packet.
*
* @return Pointer to a copy of the username. NULL if packet was of wrong
* type, if memory allocation failed or if username was empty.
*/
static char* get_username_from_auth(
char* ptr,
uint8_t* data)
{
uint8_t packet_type;
char* first_letter;
char* rval;
packet_type = data[4];
if (packet_type != MYSQL_COM_CREATE_DB)
{
rval = NULL;
goto retblock;
}
first_letter = (char *)(data + 4 + 4 + 4 + 1 + 23);
if (first_letter == '\0')
{
rval = NULL;
goto retblock;
}
if (ptr == NULL)
{
if ((rval = (char *)malloc(MYSQL_USER_MAXLEN)) == NULL)
{
goto retblock;
}
}
else
{
rval = ptr;
}
strncpy(rval, first_letter, MYSQL_USER_MAXLEN);
retblock:
return rval;
}
static char* create_auth_fail_str(
GWBUF* readbuf,
char* hostaddr,
char* sha1)
{
char* errstr;
char* uname;
const char* ferrstr = "Access denied for user '%s'@'%s' (using password: %s)";
if ( (uname = get_username_from_auth(NULL, (uint8_t *)GWBUF_DATA(readbuf))) == NULL)
{
errstr = NULL;
goto retblock;
}
/** -4 comes from 2X'%s' minus terminating char */
errstr = (char *)malloc(strlen(uname)+strlen(ferrstr)+strlen(hostaddr)+strlen("YES")-6+1);
if (errstr != NULL)
{
sprintf(errstr, ferrstr, uname, hostaddr, (*sha1 == '\0' ? "NO" : "YES"));
}
retblock:
return errstr;
}
/** /**
* Write function for client DCB: writes data from MaxScale to Client * Write function for client DCB: writes data from MaxScale to Client
* *
@ -593,77 +671,83 @@ int gw_read_client_event(
case MYSQL_AUTH_SENT: case MYSQL_AUTH_SENT:
{ {
int auth_val = -1; int auth_val;
auth_val = gw_mysql_do_authentication(dcb, read_buffer); auth_val = gw_mysql_do_authentication(dcb, read_buffer);
read_buffer = gwbuf_consume(read_buffer, nbytes_read);
ss_dassert(read_buffer == NULL || GWBUF_EMPTY(read_buffer)); if (auth_val == 0)
{
if (auth_val == 0) SESSION *session;
{
SESSION *session = NULL; protocol->protocol_auth_state = MYSQL_AUTH_RECV;
protocol->protocol_auth_state = MYSQL_AUTH_RECV; /**
/** * Create session, and a router session for it.
* Create session, and a router session for it. * If successful, there will be backend connection(s)
* If successful, there will be backend connection(s) * after this point.
* after this point. */
*/ session = session_alloc(dcb->service, dcb);
session = session_alloc(dcb->service, dcb);
if (session != NULL)
if (session != NULL) {
{ CHK_SESSION(session);
CHK_SESSION(session); ss_dassert(session->state != SESSION_STATE_ALLOC);
ss_dassert(session->state != SESSION_STATE_ALLOC);
protocol->protocol_auth_state = MYSQL_IDLE;
/**
* Send an AUTH_OK packet to the client,
* packet sequence is # 2
*/
mysql_send_ok(dcb, 2, 0, NULL);
}
else
{
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] session "
"creation failed. fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
/** Send ERR 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
"failed to create new session");
dcb_close(dcb);
}
}
else
{
char* fail_str;
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
fail_str = create_auth_fail_str(read_buffer,
dcb->remote,
(char*)((MYSQL_session *)dcb->data)->client_sha1);
/** Send error 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
fail_str);
protocol->protocol_auth_state = MYSQL_IDLE; LOGIF(LD, (skygw_log_write(
/** LOGFILE_DEBUG,
* Send an AUTH_OK packet to the client, "%lu [gw_read_client_event] after "
* packet sequence is # 2 "gw_mysql_do_authentication, fd %d, "
*/ "state = MYSQL_AUTH_FAILED.",
mysql_send_ok(dcb, 2, 0, NULL); protocol->owner_dcb->fd,
} pthread_self())));
else free(fail_str);
{ dcb_close(dcb);
protocol->protocol_auth_state = MYSQL_AUTH_FAILED; }
LOGIF(LD, (skygw_log_write( read_buffer = gwbuf_consume(read_buffer, nbytes_read);
LOGFILE_DEBUG, }
"%lu [gw_read_client_event] session "
"creation failed. fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
/** Send ERR 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
"failed to create new session");
dcb_close(dcb);
}
}
else
{
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] after "
"gw_mysql_do_authentication, fd %d, "
"state = MYSQL_AUTH_FAILED.",
protocol->owner_dcb->fd,
pthread_self())));
/** Send ERR 1045 to client */
mysql_send_auth_error(
dcb,
2,
0,
"Access denied");
dcb_close(dcb);
}
}
break; break;
case MYSQL_IDLE: case MYSQL_IDLE: