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 { } else {
LOGIF(LE, (skygw_log_write_flush( LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR, 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(), pthread_self(),
row[0], row[0],
row[1], row[1],

View File

@ -22,8 +22,9 @@
* @verbatim * @verbatim
* Revision History * Revision History
* *
* Date Who Description * Date Who Description
* 04/06/14 Mark Riddoch Initial implementation * 04/06/14 Mark Riddoch Initial implementation
* 24/10/14 Massimiliano Pinto Added modutil_send_mysql_err_packet, modutil_create_mysql_err_msg
* *
* @endverbatim * @endverbatim
*/ */
@ -229,27 +230,27 @@ retblock:
} }
/* create mysql response packet */ /**
* create mysql response packet */
*/
GWBUF* modutil_create_mysql_packet( GWBUF* modutil_create_mysql_packet(
int packet_number, int packet_number,
int affected_rows, int affected_rows,
int merrno, int merrno,
char *statemsg, const char *statemsg,
const char * msg) const char * msg)
{ {
uint8_t *outbuf = NULL; uint8_t *outbuf = NULL;
uint8_t mysql_payload_size = 0; uint8_t mysql_payload_size = 0;
uint8_t mysql_packet_header[4]; uint8_t mysql_packet_header[4];
uint8_t *mysql_payload = NULL; uint8_t *mysql_payload = NULL;
uint8_t field_count = 0; uint8_t field_count = 0;
uint8_t mysql_err[2]; uint8_t mysql_err[2];
uint8_t mysql_statemsg[6]; uint8_t mysql_statemsg[6];
unsigned int mysql_errno = 0; unsigned int mysql_errno = 0;
const char* mysql_error_msg = NULL; const char *mysql_error_msg = NULL;
const char* mysql_state = NULL; const char *mysql_state = NULL;
GWBUF *errbuf = NULL;
GWBUF* errbuf = NULL;
mysql_errno = (unsigned int)merrno; mysql_errno = (unsigned int)merrno;
mysql_error_msg = msg; mysql_error_msg = msg;
@ -270,7 +271,7 @@ GWBUF* modutil_create_mysql_packet(
sizeof(mysql_statemsg) + sizeof(mysql_statemsg) +
strlen(mysql_error_msg); 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); errbuf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size);
ss_dassert(errbuf != NULL); 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 * 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 dcb The DCB to send the packet
* @param packet_number * @param packet_number MySQL protocol sequence number in the packet
* @param in_affected_rows * @param in_affected_rows MySQL affected rows
* @param mysql_message * @param mysql_errno The MySQL errno
* @return packet length * @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, DCB *dcb,
int packet_number, int packet_number,
int in_affected_rows, int in_affected_rows,
const char *mysql_message) int mysql_errno,
const char *sqlstate_msg,
const char *mysql_message)
{ {
GWBUF* buf; 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); return dcb->func.write(dcb, buf);
} }

View File

@ -24,18 +24,21 @@
* @verbatim * @verbatim
* Revision History * Revision History
* *
* Date Who Description * Date Who Description
* 04/06/14 Mark Riddoch Initial implementation * 04/06/14 Mark Riddoch Initial implementation
* 24/06/14 Mark Riddoch Add modutil_MySQL_Query to enable multipacket queries * 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 * @endverbatim
*/ */
#include <buffer.h> #include <buffer.h>
#include <dcb.h>
extern int modutil_is_SQL(GWBUF *); extern int modutil_is_SQL(GWBUF *);
extern int modutil_extract_SQL(GWBUF *, char **, int *); extern int modutil_extract_SQL(GWBUF *, char **, int *);
extern int modutil_MySQL_Query(GWBUF *, char **, int *, int *); extern int modutil_MySQL_Query(GWBUF *, char **, int *, int *);
extern GWBUF *modutil_replace_SQL(GWBUF *, char *); 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 #endif

View File

@ -20,6 +20,8 @@
#include <skygw_types.h> #include <skygw_types.h>
#include <skygw_utils.h> #include <skygw_utils.h>
#include <log_manager.h> #include <log_manager.h>
#include <modutil.h>
/* /*
* MySQL Protocol module for handling the protocol between the gateway * MySQL Protocol module for handling the protocol between the gateway
* and the backend MySQL database. * and the backend MySQL database.
@ -41,6 +43,7 @@
* 04/09/2013 Massimiliano Pinto Added dcb->session and dcb->session->client checks for NULL * 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 * 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 * 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> #include <modinfo.h>
@ -66,7 +69,8 @@ static int backend_write_delayqueue(DCB *dcb);
static void backend_set_delayqueue(DCB *dcb, GWBUF *queue); 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 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); 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) #if defined(NOT_USED)
@ -1172,8 +1176,15 @@ static int backend_write_delayqueue(DCB *dcb)
return rc; 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( static int gw_change_user(
DCB *backend, DCB *backend,
SERVER *server, SERVER *server,
@ -1197,18 +1208,18 @@ static int gw_change_user(
backend_protocol = backend->protocol; backend_protocol = backend->protocol;
client_protocol = in_session->client->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; client_auth_packet += 5;
strcpy(username, (char *)client_auth_packet); strcpy(username, (char *)client_auth_packet);
client_auth_packet += strlen(username) + 1; client_auth_packet += strlen(username) + 1;
// get the auth token len /* get the auth token len */
memcpy(&auth_token_len, client_auth_packet, 1); memcpy(&auth_token_len, client_auth_packet, 1);
ss_dassert(auth_token_len >= 0); ss_dassert(auth_token_len >= 0);
client_auth_packet++; 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) { if (auth_token_len > 0) {
auth_token = (uint8_t *)malloc(auth_token_len); auth_token = (uint8_t *)malloc(auth_token_len);
ss_dassert(auth_token != NULL); ss_dassert(auth_token != NULL);
@ -1225,11 +1236,16 @@ static int gw_change_user(
/* save current_database name */ /* save current_database name */
strcpy(current_database, current_session->db); 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, ""); 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); 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) { if (auth_ret != 0) {
@ -1243,30 +1259,34 @@ static int gw_change_user(
/* copy back current datbase to client session */ /* copy back current datbase to client session */
strcpy(current_session->db, current_database); strcpy(current_session->db, current_database);
// let's free the auth_token now /* let's free the auth_token now */
if (auth_token) if (auth_token)
free(auth_token); free(auth_token);
if (auth_ret != 0) { if (auth_ret != 0) {
char *message; char *password_set = NULL;
char *message = NULL;
message = calloc(1,100); if (auth_token_len > 0)
strcpy(message, "change user authentication failed"); password_set = (char *)client_sha1;
else
password_set = "";
message=create_auth_fail_str(username,
backend->session->client->remote,
password_set,
"");
/* send the error packet */ /* send the error packet */
//modutil_send_mysql_packet(backend->session->client, 1, 0, message); modutil_send_mysql_err_packet(backend->session->client, 1, 0, 1045, "28000", message);
mysql_send_auth_error(backend->session->client, 1, 0, message);
fprintf(stderr, "ERROR change user for [%s] to [%s]\n", username, database);
free(message); free(message);
rv = 1; rv = 1;
} else { } 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); rv = gw_send_change_user_to_backend(database, username, client_sha1, backend_protocol);
/*< /*
* Now copy new data into user session * Now copy new data into user session
*/ */
strcpy(current_session->user, username); strcpy(current_session->user, username);
@ -1275,8 +1295,6 @@ static int gw_change_user(
} }
gwbuf_free(queue); gwbuf_free(queue);
fprintf(stderr, "--- After change_user curren client dcb DB is [%s]\n", current_session->db);
return rv; 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); 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* sha1i, char *db);
extern char* get_username_from_auth(char* ptr, uint8_t* data); 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. * 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; uint8_t *stage1_hash = NULL;
int auth_ret = -1; int auth_ret = -1;
MYSQL_session *client_data = NULL; MYSQL_session *client_data = NULL;
int db_exists = 0;
CHK_DCB(dcb); CHK_DCB(dcb);
@ -508,47 +507,6 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
return auth_ret; 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 * Write function for client DCB: writes data from MaxScale to Client
* *
@ -700,35 +658,28 @@ int gw_read_client_event(
} }
else else
{ {
char* fail_str; char* fail_str = NULL;
protocol->protocol_auth_state = MYSQL_AUTH_FAILED; protocol->protocol_auth_state = MYSQL_AUTH_FAILED;
if (auth_val == 2) { if (auth_val == 2) {
char *dberr; /** Send error 1049 to client */
dberr= calloc(1, 100); int message_len = 25 + MYSQL_DATABASE_MAXLEN;
sprintf(dberr, "Unknown database '%s'", (char*)((MYSQL_session *)dcb->data)->db);
modutil_send_mysql_packet( fail_str = calloc(1, message_len+1);
//mysql_send_auth_error( snprintf(fail_str, message_len, "Unknown database '%s'", (char*)((MYSQL_session *)dcb->data)->db);
dcb,
2,
0,
dberr);
free(dberr); modutil_send_mysql_err_packet(dcb, 2, 0, 1049, "42000", fail_str);
} else { } else {
/** Send error 1045 to client */ /** 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, dcb->remote,
(char*)((MYSQL_session *)dcb->data)->client_sha1, (char*)((MYSQL_session *)dcb->data)->db); (char*)((MYSQL_session *)dcb->data)->client_sha1,
mysql_send_auth_error( (char*)((MYSQL_session *)dcb->data)->db);
dcb, modutil_send_mysql_err_packet(dcb, 2, 0, 1045, "28000", fail_str);
2,
0,
fail_str);
free(fail_str);
} }
if (fail_str)
free(fail_str);
LOGIF(LD, (skygw_log_write( LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG, LOGFILE_DEBUG,

View File

@ -34,6 +34,7 @@
* 29/09/2014 Massimiliano Pinto Added Mysql user@host authentication with wildcard in IPv4 hosts: * 29/09/2014 Massimiliano Pinto Added Mysql user@host authentication with wildcard in IPv4 hosts:
* x.y.z.%, x.y.%.%, x.%.%.% * x.y.z.%, x.y.%.%, x.%.%.%
* 03/10/2014 Massimiliano Pinto Added netmask for wildcard in IPv4 hosts. * 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( char* create_auth_failed_msg(
GWBUF* readbuf, GWBUF* readbuf,
char* hostaddr, char* hostaddr,
uint8_t* sha1, int dbmatch) uint8_t* sha1)
{ {
char* errstr; char* errstr;
char* uname=(char *)GWBUF_DATA(readbuf) + 5; char* uname=(char *)GWBUF_DATA(readbuf) + 5;
const char* ferrstr = "Access denied for user '%s'@'%s' (using password: %s)"; const char* ferrstr = "Access denied for user '%s'@'%s' (using password: %s)";
/** -4 comes from 2X'%s' minus terminating char */ /** -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) if (errstr != NULL)
{ {
sprintf(errstr, ferrstr, uname, hostaddr, (*sha1 == '\0' ? "NO" : "YES")); sprintf(errstr, ferrstr, uname, hostaddr, (*sha1 == '\0' ? "NO" : "YES"));
strcat(errstr, " to database 'database'");
} }
return errstr; return errstr;
@ -2007,6 +2007,8 @@ char* create_auth_failed_msg(
/** /**
* Read username from MySQL authentication packet. * 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 * @param ptr address where to write the result or NULL if memory
* is allocated here. * is allocated here.
* @param data Address of MySQL packet. * @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; 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;
}