MySQL authentication with db name

MySQL authentication with db name
This commit is contained in:
MassimilianoPinto 2014-10-21 16:46:52 +02:00
parent 3cdb1dc2ae
commit a1f621da30
2 changed files with 93 additions and 124 deletions

View File

@ -53,7 +53,7 @@
#define MYSQL_USERS_COUNT "SELECT COUNT(1) AS nusers FROM mysql.user"
#define MYSQL_USERS_WITH_DB_ORDER " ORDER BY host DESC"
#define LOAD_MYSQL_USERS_WITH_DB_QUERY "SELECT user.user AS user,user.host AS host,user.password AS password,concat(user.user,user.host,user.password,user.Select_priv) AS userdata, user.Select_priv AS anydb,db.db AS db FROM mysql.user LEFT JOIN mysql.db ON user.user=db.user AND user.host=db.host WHERE user.user IS NOT NULL AND user.user <> ''" MYSQL_USERS_WITH_DB_ORDER
#define LOAD_MYSQL_USERS_WITH_DB_QUERY "SELECT user.user AS user,user.host AS host,user.password AS password,concat(user.user,user.host,user.password,user.Select_priv,IFNULL(db,'')) AS userdata, user.Select_priv AS anydb,db.db AS db FROM mysql.user LEFT JOIN mysql.db ON user.user=db.user AND user.host=db.host WHERE user.user IS NOT NULL AND user.user <> ''" MYSQL_USERS_WITH_DB_ORDER
#define LOAD_MYSQL_USERS_WITH_DB_QUERY_NO_ROOT "SELECT * FROM (" LOAD_MYSQL_USERS_WITH_DB_QUERY ") AS t1 WHERE user NOT IN ('root')" MYSQL_USERS_WITH_DB_ORDER
@ -67,7 +67,7 @@ static int uh_hfun( void* key);
char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key);
char *mysql_format_user_entry(void *data);
int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *passwd, char *anydb, char *db);
static int getDatabases(SERVICE *);
static int getDatabases(SERVICE *, MYSQL *);
HASHTABLE *resource_alloc();
/**
@ -83,8 +83,8 @@ load_mysql_users(SERVICE *service)
return getUsers(service, service->users);
}
int mysql_users_load_dbs(SERVICE *service) {
return getDatabases(service);
int mysql_users_load_dbs(SERVICE *service, MYSQL *con) {
return getDatabases(service, con);
}
/**
@ -207,10 +207,9 @@ int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *p
/* for anydb == Y key.resource is '\0' as set by memset */
if (anydb == NULL) {
key.resource = NULL;
}
else {
} else {
if (strcmp(anydb, "N") == 0) {
if (db)
if (db != NULL)
key.resource = strdup(db);
else
key.resource = NULL;
@ -265,7 +264,7 @@ int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *p
/* add user@host as key and passwd as value in the MySQL users hash table */
if (mysql_users_add(users, &key, passwd)) {
ret = 1;
fprintf(stderr, "Added user %s@%i with db [%s]\n", key.user, key.ipv4.sin_addr.s_addr, key.resource);
fprintf(stderr, "Added user %s@%i with db [%s]\n", key.user, key.ipv4.sin_addr.s_addr, key.resource == NULL ? "NULL" : key.resource);
}
}
@ -285,15 +284,12 @@ int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *p
* @return -1 on any error or the number of users inserted (0 means no users at all)
*/
static int
getDatabases(SERVICE *service)
getDatabases(SERVICE *service, MYSQL *con)
{
MYSQL *con = NULL;
MYSQL_ROW row;
MYSQL_RES *result = NULL;
char *service_user = NULL;
char *service_passwd = NULL;
char *dpwd;
SERVER *server;
int ndbs = 0;
int i = 0;
@ -303,55 +299,6 @@ getDatabases(SERVICE *service)
if (service_user == NULL || service_passwd == NULL)
return -1;
con = mysql_init(NULL);
if (con == NULL) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : mysql_init: %s",
mysql_error(con))));
return -1;
}
if (mysql_options(con, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL)) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : failed to set external connection. "
"It is needed for backend server connections. "
"Exiting.")));
return -1;
}
/**
* Attempt to connect to one of the databases database or until we run
* out of databases
* to try
*/
server = service->databases;
dpwd = decryptPassword(service_passwd);
while (server != NULL && (mysql_real_connect(con,
server->name,
service_user,
dpwd,
NULL,
server->port,
NULL,
0) == NULL))
{
server = server->nextdb;
}
free(dpwd);
if (server == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Unable to load db names from backend database "
"for service %s. Missing server information.",
service->name)));
mysql_close(con);
return -1;
}
if (mysql_query(con, get_showdbs_priv_query)) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
@ -359,7 +306,6 @@ getDatabases(SERVICE *service)
"error: %s.",
service->name,
mysql_error(con))));
mysql_close(con);
return -1;
}
@ -372,7 +318,6 @@ getDatabases(SERVICE *service)
"error: %s.",
service->name,
mysql_error(con))));
mysql_close(con);
return -1;
}
@ -397,12 +342,9 @@ getDatabases(SERVICE *service)
if (!ndbs) {
/* return if no db names are available */
mysql_close(con);
return 0;
}
if (mysql_query(con, "SHOW DATABASES")) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
@ -411,7 +353,6 @@ getDatabases(SERVICE *service)
service->name,
mysql_error(con))));
mysql_close(con);
return -1;
}
@ -425,31 +366,20 @@ getDatabases(SERVICE *service)
service->name,
mysql_error(con))));
/* cleanup the hashtable */
mysql_close(con);
return -1;
}
/* Now populate service->resources hashatable with db names */
//service->resources = (char **) calloc(ndbs+1, sizeof(char *));
service->resources = resource_alloc();
// if calloc fails, return -1
//for (i = 0; i < ndbs; i++) {
// service->resources[i] = NULL;
//}
i = 0;
while ((row = mysql_fetch_row(result))) {
//service->resources[i] = strndup(row[0], MYSQL_DATABASE_MAXLEN);
fprintf(stderr, "Found a Database[%i]:[%s]\n", i, row[0]);
resource_add(service->resources, row[0], "");
fprintf(stderr, "Found a Database[%i]:[%s]\n", i, row[0]);
i++;
}
mysql_free_result(result);
mysql_close(con);
return ndbs;
}
@ -477,9 +407,9 @@ getUsers(SERVICE *service, USERS *users)
unsigned char hash[SHA_DIGEST_LENGTH]="";
char *users_data = NULL;
int nusers = 0;
/* last byte is for Select_priv=Y|N */
int users_data_row_len = MYSQL_USER_MAXLEN + MYSQL_HOST_MAXLEN + MYSQL_PASSWORD_LEN + sizeof(char);
int dbnames_loaded = 0;
int users_data_row_len = MYSQL_USER_MAXLEN + MYSQL_HOST_MAXLEN + MYSQL_PASSWORD_LEN + sizeof(char) + MYSQL_DATABASE_MAXLEN;
int dbnames = 0;
int db_grants = 0;
serviceGetUser(service, &service_user, &service_passwd);
@ -487,14 +417,6 @@ getUsers(SERVICE *service, USERS *users)
if (service_user == NULL || service_passwd == NULL)
return -1;
/* load all mysql database names */
dbnames_loaded = mysql_users_load_dbs(service);
LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE,
"Loaded %d MySQL Database Names.",
dbnames_loaded)));
con = mysql_init(NULL);
if (con == NULL) {
@ -545,6 +467,15 @@ getUsers(SERVICE *service, USERS *users)
return -1;
}
/* load all mysql database names */
dbnames = mysql_users_load_dbs(service, con);
LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE,
"Loaded %d MySQL Database Names.",
dbnames)));
/* Load users */
if (mysql_query(con, MYSQL_USERS_COUNT)) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
@ -583,17 +514,16 @@ getUsers(SERVICE *service, USERS *users)
return -1;
}
fprintf(stderr, "---> result from dbnames_loaded = [%i]\n", dbnames_loaded);
/* enable_root for MySQL protocol module means load the root user credentials from backend databases */
if (dbnames_loaded > 0) {
fprintf(stderr, "Loading dbnames_loaded = [%i]\n", dbnames_loaded);
if (dbnames > 0) {
/* check for root user select */
if(service->enable_root) {
users_query = LOAD_MYSQL_USERS_WITH_DB_QUERY;
} else {
users_query = LOAD_MYSQL_USERS_WITH_DB_QUERY_NO_ROOT;
}
} else {
/* check for root user select */
if(service->enable_root) {
users_query = LOAD_MYSQL_USERS_QUERY " ORDER BY HOST DESC";
} else {
@ -601,10 +531,17 @@ getUsers(SERVICE *service, USERS *users)
}
}
/* if there is a Table access denied, try first removing dbanme from user selection */
/* send the query that fetches users and db grants */
if (mysql_query(con, users_query)) {
/* if not ER_TABLEACCESS_DENIED_ERROR) exit */
/*
* An error occurred executing the query
*
* Check mysql_errno() against ER_TABLEACCESS_DENIED_ERROR)
*/
if (1142 != mysql_errno(con)) {
/* This is an error we cannot handle, return */
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Loading users with dbnames for service [%s] encountered "
@ -617,11 +554,16 @@ getUsers(SERVICE *service, USERS *users)
return -1;
} else {
/* ER_TABLEACCESS_DENIED_ERROR, try mysql.user only query without DB names */
/*
* We have got ER_TABLEACCESS_DENIED_ERROR
* try loading users from mysql.user without DB names.
*/
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Warning: Loading DB grants failed: GRANT is required on [mysql.db] to user [%s]. Try loading DB users for service [%s] without DB name MaxScale Authentication", service_user, service->name)));
/* check for root user select */
if(service->enable_root) {
users_query = LOAD_MYSQL_USERS_QUERY " ORDER BY HOST DESC";
} else {
@ -642,9 +584,29 @@ getUsers(SERVICE *service, USERS *users)
return -1;
}
/* users loaded but without dbnames */
dbnames_loaded = 0;
// LOG IT
/* users successfully loaded but without db grants */
}
} else {
/*
* users successfully loaded.
* If dbnames > 0 the query loaded also db grants.
*/
if (dbnames > 0) {
// LOG IT
db_grants = 1;
}
}
/*
* If database specific grants were not loaded
* we need to delete database names hashtable, if loaded
*/
if (db_grants == 0 && dbnames > 0) {
// LOG IT
resource_free(service->resources);
}
result = mysql_store_result(con);
@ -680,11 +642,20 @@ getUsers(SERVICE *service, USERS *users)
*/
int rc = 0;
char *password;
if (row[2] != NULL) {
if (strlen(row[2]) > 1)
password = row[2] +1;
else
password = row[2];
} else {
password = strdup("");
}
if (dbnames_loaded)
rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], strlen(row[2]) ? row[2]+1 : row[2], row[4], row[5]);
if (db_grants > 0)
rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, row[4], row[5]);
else
rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], strlen(row[2]) ? row[2]+1 : row[2], "Y", NULL);
rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, "Y", NULL);
if (rc == 1) {
LOGIF(LE, (skygw_log_write_flush(
@ -817,9 +788,15 @@ static int uh_cmpfun( void* v1, void* v2) {
MYSQL_USER_HOST *hu1 = (MYSQL_USER_HOST *) v1;
MYSQL_USER_HOST *hu2 = (MYSQL_USER_HOST *) v2;
if (v1 == NULL || v2 == NULL || hu1 == NULL || hu2 == NULL || hu1->user == NULL || hu2->user == NULL)
if (v1 == NULL || v2 == NULL)
return 0;
if (hu1 == NULL || hu2 == NULL)
return 0;
if (hu1->user == NULL || hu2->user == NULL)
return 0;
if (strcmp(hu1->user, hu2->user) == 0 && (hu1->ipv4.sin_addr.s_addr == hu2->ipv4.sin_addr.s_addr) && (hu1->netmask >= hu2->netmask)) {
/* if no database name was passed, auth is ok */

View File

@ -456,7 +456,6 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
strncpy(database,
(char *)(client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) +
1 + 1 + auth_token_len), MYSQL_DATABASE_MAXLEN);
fprintf(stderr, "---- Database name passed [%s], flag [%i]\n", database, connect_with_db);
}
/* allocate memory for token only if auth_token_len > 0 */
@ -487,36 +486,30 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
}
}
fprintf(stderr, "--- Authentication reply is [%i]\n", auth_ret);
/* let's free the auth_token now */
if (auth_token)
free(auth_token);
if (database && strlen(database)) {
int i = 0;
/* if database names are loaded we can check if db name exists */
if (dcb->service->resources) {
if (hashtable_fetch(dcb->service->resources, database)) {
db_exists = 1;
db_exists = 1;
} else {
db_exists = 0;
}
/* fetch dbname */
/*
while(dcb->service->resources[i]) {
if (strncmp(database, dcb->service->resources[i], MYSQL_DATABASE_MAXLEN) == 0) {
db_exists = 1;
}
i++;
}
*/
} else {
/* if database names are not loaded we take dbname as existent */
db_exists = 1;
/* if database names are not loaded we don't allow connection */
db_exists = -1;
}
if (!db_exists && auth_ret == 0) {
if (db_exists == 0 && auth_ret == 0) {
auth_ret = 2;
}
if (db_exists < 0 && auth_ret == 0) {
auth_ret = 1;
}
}
if (auth_ret == 0)
@ -753,8 +746,7 @@ int gw_read_client_event(
uint8_t* payload = NULL;
bool stmt_input; /*< router input type */
fprintf(stderr, "NBYTES %i\n", nbytes_read);
//ss_dassert(nbytes_read >= 5);
ss_dassert(nbytes_read >= 5);
session = dcb->session;
ss_dassert( session!= NULL);