Update for message errors in DB authentication

Update for message errors in DB authentication
This commit is contained in:
MassimilianoPinto
2014-10-24 12:20:50 +02:00
parent 48423f75f6
commit 970511a275
6 changed files with 142 additions and 122 deletions

View File

@ -731,7 +731,7 @@ getUsers(SERVICE *service, USERS *users)
} else {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"%lu [mysql_users_add()] Failed adding user %s@%si for service [%s]",
"%lu [mysql_users_add()] Failed adding user %s@%s for service [%s]",
pthread_self(),
row[0],
row[1],

View File

@ -24,6 +24,7 @@
*
* Date Who Description
* 04/06/14 Mark Riddoch Initial implementation
* 24/10/14 Massimiliano Pinto Added modutil_send_mysql_err_packet, modutil_create_mysql_err_msg
*
* @endverbatim
*/
@ -229,13 +230,14 @@ retblock:
}
/* create mysql response packet */
/**
* create mysql response packet */
*/
GWBUF* modutil_create_mysql_packet(
int packet_number,
int affected_rows,
int merrno,
char *statemsg,
const char *statemsg,
const char * msg)
{
uint8_t *outbuf = NULL;
@ -246,10 +248,9 @@ GWBUF* modutil_create_mysql_packet(
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* errbuf = NULL;
const char *mysql_error_msg = NULL;
const char *mysql_state = NULL;
GWBUF *errbuf = NULL;
mysql_errno = (unsigned int)merrno;
mysql_error_msg = msg;
@ -270,7 +271,7 @@ GWBUF* modutil_create_mysql_packet(
sizeof(mysql_statemsg) +
strlen(mysql_error_msg);
/** allocate memory for packet header + payload */
/* allocate memory for packet header + payload */
errbuf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size);
ss_dassert(errbuf != NULL);
@ -308,27 +309,30 @@ GWBUF* modutil_create_mysql_packet(
}
/**
* mysql_send_custom_error
* modutil_send_mysql_err_packet
*
* Send a MySQL protocol Generic ERR message, to the dcb
* Note the errno and state are still fixed now
*
* @param dcb Owner_Dcb Control Block for the connection to which the OK is sent
* @param packet_number
* @param in_affected_rows
* @param mysql_message
* @return packet length
* @param dcb The DCB to send the packet
* @param packet_number MySQL protocol sequence number in the packet
* @param in_affected_rows MySQL affected rows
* @param mysql_errno The MySQL errno
* @param sqlstate_msg The MySQL State Message
* @param mysql_message The Error Message
* @return 0 for successful dcb write or 1 on failure
*
*/
int modutil_send_mysql_packet (
int modutil_send_mysql_err_packet (
DCB *dcb,
int packet_number,
int in_affected_rows,
int mysql_errno,
const char *sqlstate_msg,
const char *mysql_message)
{
GWBUF* buf;
buf = modutil_create_mysql_packet(packet_number, in_affected_rows, 1049, "42000", mysql_message);
buf = modutil_create_mysql_err_msg(packet_number, in_affected_rows, mysql_errno, sqlstate_msg, mysql_message);
return dcb->func.write(dcb, buf);
}

View File

@ -27,15 +27,18 @@
* Date Who Description
* 04/06/14 Mark Riddoch Initial implementation
* 24/06/14 Mark Riddoch Add modutil_MySQL_Query to enable multipacket queries
* 24/10/14 Massimiliano Pinto Add modutil_send_mysql_err_packet to send a mysql ERR_Packet
*
* @endverbatim
*/
#include <buffer.h>
#include <dcb.h>
extern int modutil_is_SQL(GWBUF *);
extern int modutil_extract_SQL(GWBUF *, char **, int *);
extern int modutil_MySQL_Query(GWBUF *, char **, int *, int *);
extern GWBUF *modutil_replace_SQL(GWBUF *, char *);
char* modutil_get_query(GWBUF* buf);
extern char *modutil_get_query(GWBUF* buf);
extern int modutil_send_mysql_err_packet(DCB *, int, int, int, const char *, const char *);
#endif

View File

@ -20,6 +20,8 @@
#include <skygw_types.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <modutil.h>
/*
* MySQL Protocol module for handling the protocol between the gateway
* and the backend MySQL database.
@ -41,6 +43,7 @@
* 04/09/2013 Massimiliano Pinto Added dcb->session and dcb->session->client checks for NULL
* 12/09/2013 Massimiliano Pinto Added checks in gw_read_backend_event() for gw_read_backend_handshake
* 27/09/2013 Massimiliano Pinto Changed in gw_read_backend_event the check for dcb_read(), now is if rc < 0
* 24/10/2014 Massimiliano Pinto Added Mysql user@host @db authentication support
*
*/
#include <modinfo.h>
@ -66,7 +69,8 @@ static int backend_write_delayqueue(DCB *dcb);
static void backend_set_delayqueue(DCB *dcb, GWBUF *queue);
static int gw_change_user(DCB *backend_dcb, SERVER *server, SESSION *in_session, GWBUF *queue);
static GWBUF* process_response_data (DCB* dcb, GWBUF* readbuf, int nbytes_to_process);
extern char* create_auth_failed_msg( GWBUF* readbuf, char* hostaddr, uint8_t* sha1, int dbmatch);
extern char* create_auth_failed_msg( GWBUF* readbuf, char* hostaddr, uint8_t* sha1);
extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db);
#if defined(NOT_USED)
@ -1172,8 +1176,15 @@ static int backend_write_delayqueue(DCB *dcb)
return rc;
}
/**
* This routine handles the COM_CHANGE_USER command
*
* @param dcb The current backend DCB
* @param server The backend server pointer
* @param in_session The current session data (MYSQL_session)
* @param queue The GWBUF containing the COM_CHANGE_USER receveid
* @return 0 on success and 1 on failure
*/
static int gw_change_user(
DCB *backend,
SERVER *server,
@ -1197,18 +1208,18 @@ static int gw_change_user(
backend_protocol = backend->protocol;
client_protocol = in_session->client->protocol;
// now get the user, after 4 bytes header and 1 byte command
/* now get the user, after 4 bytes header and 1 byte command */
client_auth_packet += 5;
strcpy(username, (char *)client_auth_packet);
client_auth_packet += strlen(username) + 1;
// get the auth token len
/* get the auth token len */
memcpy(&auth_token_len, client_auth_packet, 1);
ss_dassert(auth_token_len >= 0);
client_auth_packet++;
// allocate memory for token only if auth_token_len > 0
/* allocate memory for token only if auth_token_len > 0 */
if (auth_token_len > 0) {
auth_token = (uint8_t *)malloc(auth_token_len);
ss_dassert(auth_token != NULL);
@ -1225,11 +1236,16 @@ static int gw_change_user(
/* save current_database name */
strcpy(current_database, current_session->db);
/* empty database name in dcb */
/*
* Now clear database name in dcb as we don't do local authentication on db name for change user.
* Local authentication only for user@host and if successful the database name change is sent to backend.
*/
strcpy(current_session->db, "");
// decode the token and check the password
// Note: if auth_token_len == 0 && auth_token == NULL, user is without password
/*
* decode the token and check the password.
* Note: if auth_token_len == 0 && auth_token == NULL, user is without password
*/
auth_ret = gw_check_mysql_scramble_data(backend->session->client, auth_token, auth_token_len, client_protocol->scramble, sizeof(client_protocol->scramble), username, client_sha1);
if (auth_ret != 0) {
@ -1243,30 +1259,34 @@ static int gw_change_user(
/* copy back current datbase to client session */
strcpy(current_session->db, current_database);
// let's free the auth_token now
/* let's free the auth_token now */
if (auth_token)
free(auth_token);
if (auth_ret != 0) {
char *message;
char *password_set = NULL;
char *message = NULL;
message = calloc(1,100);
strcpy(message, "change user authentication failed");
if (auth_token_len > 0)
password_set = (char *)client_sha1;
else
password_set = "";
message=create_auth_fail_str(username,
backend->session->client->remote,
password_set,
"");
/* send the error packet */
//modutil_send_mysql_packet(backend->session->client, 1, 0, message);
mysql_send_auth_error(backend->session->client, 1, 0, message);
fprintf(stderr, "ERROR change user for [%s] to [%s]\n", username, database);
modutil_send_mysql_err_packet(backend->session->client, 1, 0, 1045, "28000", message);
free(message);
rv = 1;
} else {
fprintf(stderr, "going to backend change_user for db [%s]\n", database);
rv = gw_send_change_user_to_backend(database, username, client_sha1, backend_protocol);
/*<
/*
* Now copy new data into user session
*/
strcpy(current_session->user, username);
@ -1275,8 +1295,6 @@ static int gw_change_user(
}
gwbuf_free(queue);
fprintf(stderr, "--- After change_user curren client dcb DB is [%s]\n", current_session->db);
return rv;
}

View File

@ -70,9 +70,9 @@ int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char*
int MySQLSendHandshake(DCB* dcb);
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
static int route_by_statement(SESSION *, GWBUF **);
static char* create_auth_fail_str(GWBUF* readbuf, char* hostaddr, char* sha1i, char *db);
extern char* get_username_from_auth(char* ptr, uint8_t* data);
extern int modutil_send_mysql_packet(DCB *, int, int, const char *);
extern int check_db_name_after_auth(DCB *, char *, int);
extern char* create_auth_fail_str(char *username, char *hostaddr, char *sha1, char *db);
/*
* The "module object" for the mysqld client protocol module.
@ -396,7 +396,6 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
uint8_t *stage1_hash = NULL;
int auth_ret = -1;
MYSQL_session *client_data = NULL;
int db_exists = 0;
CHK_DCB(dcb);
@ -508,47 +507,6 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
return auth_ret;
}
static char* create_auth_fail_str(
GWBUF* readbuf,
char* hostaddr,
char* sha1,
char* db)
{
char* errstr;
char* uname;
const char* ferrstr;
int db_len;
if (db != NULL)
db_len = strlen(db);
else
db_len = 0;
if (db_len>0)
ferrstr = "Access denied for user '%s'@'%s' (using password: %s) to database '%s'";
else
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 + db_len + 1);
if (errstr != NULL && db_len>0)
{
sprintf(errstr, ferrstr, uname, hostaddr, (*sha1 == '\0' ? "NO" : "YES"), db);
}
else
sprintf(errstr, ferrstr, uname, hostaddr, (*sha1 == '\0' ? "NO" : "YES"));
free(uname);
retblock:
return errstr;
}
/**
* Write function for client DCB: writes data from MaxScale to Client
*
@ -700,35 +658,28 @@ int gw_read_client_event(
}
else
{
char* fail_str;
char* fail_str = NULL;
protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
if (auth_val == 2) {
char *dberr;
dberr= calloc(1, 100);
sprintf(dberr, "Unknown database '%s'", (char*)((MYSQL_session *)dcb->data)->db);
/** Send error 1049 to client */
int message_len = 25 + MYSQL_DATABASE_MAXLEN;
modutil_send_mysql_packet(
//mysql_send_auth_error(
dcb,
2,
0,
dberr);
fail_str = calloc(1, message_len+1);
snprintf(fail_str, message_len, "Unknown database '%s'", (char*)((MYSQL_session *)dcb->data)->db);
free(dberr);
modutil_send_mysql_err_packet(dcb, 2, 0, 1049, "42000", fail_str);
} else {
/** Send error 1045 to client */
fail_str = create_auth_fail_str(read_buffer,
fail_str = create_auth_fail_str((char *)((MYSQL_session *)dcb->data)->user,
dcb->remote,
(char*)((MYSQL_session *)dcb->data)->client_sha1, (char*)((MYSQL_session *)dcb->data)->db);
mysql_send_auth_error(
dcb,
2,
0,
fail_str);
free(fail_str);
(char*)((MYSQL_session *)dcb->data)->client_sha1,
(char*)((MYSQL_session *)dcb->data)->db);
modutil_send_mysql_err_packet(dcb, 2, 0, 1045, "28000", fail_str);
}
if (fail_str)
free(fail_str);
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,

View File

@ -34,6 +34,7 @@
* 29/09/2014 Massimiliano Pinto Added Mysql user@host authentication with wildcard in IPv4 hosts:
* x.y.z.%, x.y.%.%, x.%.%.%
* 03/10/2014 Massimiliano Pinto Added netmask for wildcard in IPv4 hosts.
* 24/10/2014 Massimiliano Pinto Added Mysql user@host @db authentication support
*
*/
@ -1986,19 +1987,18 @@ void protocol_set_response_status (
char* create_auth_failed_msg(
GWBUF* readbuf,
char* hostaddr,
uint8_t* sha1, int dbmatch)
uint8_t* sha1)
{
char* errstr;
char* uname=(char *)GWBUF_DATA(readbuf) + 5;
const char* ferrstr = "Access denied for user '%s'@'%s' (using password: %s)";
/** -4 comes from 2X'%s' minus terminating char */
errstr = (char *)malloc(strlen(uname)+strlen(ferrstr)+strlen(hostaddr)+strlen("YES")-6+1 + strlen(" to database ") + strlen("''") + strlen("datbase") +1);
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"));
strcat(errstr, " to database 'database'");
}
return errstr;
@ -2007,6 +2007,8 @@ char* create_auth_failed_msg(
/**
* Read username from MySQL authentication packet.
*
* Only for client to server packet, COM_CHANGE_USER packet has different format.
*
* @param ptr address where to write the result or NULL if memory
* is allocated here.
* @param data Address of MySQL packet.
@ -2075,3 +2077,45 @@ int check_db_name_after_auth(DCB *dcb, char *database, int auth_ret) {
return auth_ret;
}
/**
* Create a message error string to send via MySQL ERR packet.
*
* @param username the MySQL user
* @param hostaddr the client IP
* @param sha1 authentication scramble data
* @param db the MySQL db to connect to
*
* @return Pointer to the allocated string or NULL on failure
*/
char *create_auth_fail_str(
char *username,
char *hostaddr,
char *sha1,
char *db)
{
char* errstr;
const char* ferrstr;
int db_len;
if (db != NULL)
db_len = strlen(db);
else
db_len = 0;
if (db_len>0)
ferrstr = "Access denied for user '%s'@'%s' (using password: %s) to database '%s'";
else
ferrstr = "Access denied for user '%s'@'%s' (using password: %s)";
errstr = (char *)malloc(strlen(username)+strlen(ferrstr)+strlen(hostaddr)+strlen("YES")-6 + db_len + ((db_len > 0) ? (strlen(" to database ") +2) : 0) + 1);
if (errstr != NULL) {
if (db_len>0)
sprintf(errstr, ferrstr, username, hostaddr, (*sha1 == '\0' ? "NO" : "YES"), db);
else
sprintf(errstr, ferrstr, username, hostaddr, (*sha1 == '\0' ? "NO" : "YES"));
}
return errstr;
}