Remove unused code from MySQLAuth
Removed the old implementation of MySQL authentication. The user printing functionality still expects a hashtable which should be fixed.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -328,7 +328,6 @@ mysql_auth_authenticate(DCB *dcb)
|
|||||||
static int
|
static int
|
||||||
mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||||
{
|
{
|
||||||
uint8_t *client_auth_packet = GWBUF_DATA(buf);
|
|
||||||
MySQLProtocol *protocol = NULL;
|
MySQLProtocol *protocol = NULL;
|
||||||
MYSQL_session *client_data = NULL;
|
MYSQL_session *client_data = NULL;
|
||||||
int client_auth_packet_size = 0;
|
int client_auth_packet_size = 0;
|
||||||
@ -458,293 +457,6 @@ mysql_auth_is_client_ssl_capable(DCB *dcb)
|
|||||||
return (protocol->client_capabilities & (int)GW_MYSQL_CAPABILITIES_SSL) ? true : false;
|
return (protocol->client_capabilities & (int)GW_MYSQL_CAPABILITIES_SSL) ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* gw_find_mysql_user_password_sha1
|
|
||||||
*
|
|
||||||
* The routine fetches an user from the MaxScale users' table
|
|
||||||
* The users' table is dcb->listener->users or a different one specified with void *repository
|
|
||||||
* The user lookup uses username,host and db name (if passed in connection or change user)
|
|
||||||
*
|
|
||||||
* If found the HEX password, representing sha1(sha1(password)), is converted in binary data and
|
|
||||||
* copied into gateway_password
|
|
||||||
*
|
|
||||||
* @param username The user to look for
|
|
||||||
* @param gateway_password The related SHA1(SHA1(password)), the pointer must be preallocated
|
|
||||||
* @param dcb Current DCB
|
|
||||||
* @return 1 if user is not found or 0 if the user exists
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
int gw_find_mysql_user_password_sha1(const char *username, uint8_t *gateway_password, DCB *dcb)
|
|
||||||
{
|
|
||||||
MYSQL_session *client_data = (MYSQL_session *) dcb->data;
|
|
||||||
SERVICE *service = (SERVICE *) dcb->service;
|
|
||||||
SERV_LISTENER *listener = dcb->listener;
|
|
||||||
struct sockaddr_in *client = (struct sockaddr_in *) &dcb->ipv4;
|
|
||||||
|
|
||||||
MYSQL_USER_HOST key = {};
|
|
||||||
key.user = (char*)username;
|
|
||||||
memcpy(&key.ipv4, client, sizeof(struct sockaddr_in));
|
|
||||||
key.netmask = 32;
|
|
||||||
key.resource = client_data->db;
|
|
||||||
|
|
||||||
if (strlen(dcb->remote) < MYSQL_HOST_MAXLEN)
|
|
||||||
{
|
|
||||||
strcpy(key.hostname, dcb->remote);
|
|
||||||
}
|
|
||||||
|
|
||||||
MXS_DEBUG("%lu [MySQL Client Auth], checking user [%s@%s]%s%s",
|
|
||||||
pthread_self(),
|
|
||||||
key.user,
|
|
||||||
dcb->remote,
|
|
||||||
key.resource != NULL ? " db: " : "",
|
|
||||||
key.resource != NULL ? key.resource : "");
|
|
||||||
|
|
||||||
/* look for user@current_ipv4 now */
|
|
||||||
char *user_password = mysql_users_fetch(listener->users, &key);
|
|
||||||
|
|
||||||
if (!user_password)
|
|
||||||
{
|
|
||||||
/* The user is not authenticated @ current IPv4 */
|
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* (1) Check for localhost first: 127.0.0.1 (IPv4 only)
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ((key.ipv4.sin_addr.s_addr == 0x0100007F) &&
|
|
||||||
!dcb->service->localhost_match_wildcard_host)
|
|
||||||
{
|
|
||||||
/* Skip the wildcard check and return 1 */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (2) check for possible IPv4 class C,B,A networks
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Class C check */
|
|
||||||
key.ipv4.sin_addr.s_addr &= 0x00FFFFFF;
|
|
||||||
key.netmask -= 8;
|
|
||||||
|
|
||||||
user_password = mysql_users_fetch(listener->users, &key);
|
|
||||||
|
|
||||||
if (user_password)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Class B check */
|
|
||||||
key.ipv4.sin_addr.s_addr &= 0x0000FFFF;
|
|
||||||
key.netmask -= 8;
|
|
||||||
|
|
||||||
user_password = mysql_users_fetch(listener->users, &key);
|
|
||||||
|
|
||||||
if (user_password)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Class A check */
|
|
||||||
key.ipv4.sin_addr.s_addr &= 0x000000FF;
|
|
||||||
key.netmask -= 8;
|
|
||||||
|
|
||||||
user_password = mysql_users_fetch(listener->users, &key);
|
|
||||||
|
|
||||||
if (user_password)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (3) Continue check for wildcard host, user@%
|
|
||||||
*/
|
|
||||||
|
|
||||||
memset(&key.ipv4, 0, sizeof(struct sockaddr_in));
|
|
||||||
key.netmask = 0;
|
|
||||||
|
|
||||||
MXS_DEBUG("%lu [MySQL Client Auth], checking user [%s@%s] with "
|
|
||||||
"wildcard host [%%]",
|
|
||||||
pthread_self(),
|
|
||||||
key.user,
|
|
||||||
dcb->remote);
|
|
||||||
|
|
||||||
user_password = mysql_users_fetch(listener->users, &key);
|
|
||||||
|
|
||||||
if (user_password)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user_password)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* user@% not found.
|
|
||||||
*/
|
|
||||||
|
|
||||||
MXS_DEBUG("%lu [MySQL Client Auth], user [%s@%s] not existent",
|
|
||||||
pthread_self(),
|
|
||||||
key.user,
|
|
||||||
dcb->remote);
|
|
||||||
|
|
||||||
MXS_INFO("Authentication Failed: user [%s@%s] not found.",
|
|
||||||
key.user,
|
|
||||||
dcb->remote);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If user@host has been found we get the the password in binary format*/
|
|
||||||
if (user_password)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Convert the hex data (40 bytes) to binary (20 bytes).
|
|
||||||
* The gateway_password represents the SHA1(SHA1(real_password)).
|
|
||||||
* Please note: the real_password is unknown and SHA1(real_password) is unknown as well
|
|
||||||
*/
|
|
||||||
int passwd_len = strlen(user_password);
|
|
||||||
if (passwd_len)
|
|
||||||
{
|
|
||||||
passwd_len = (passwd_len <= (SHA_DIGEST_LENGTH * 2)) ? passwd_len : (SHA_DIGEST_LENGTH * 2);
|
|
||||||
gw_hex2bin(gateway_password, user_password, passwd_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @brief Check authentication token received against stage1_hash and scramble
|
|
||||||
*
|
|
||||||
* @param dcb The current dcb
|
|
||||||
* @param token The token sent by the client in the authentication request
|
|
||||||
* @param token_len The token size in bytes
|
|
||||||
* @param scramble The scramble data sent by the server during handshake
|
|
||||||
* @param scramble_len The scramble size in bytes
|
|
||||||
* @param username The current username in the authentication request
|
|
||||||
* @param stage1_hash The SHA1(candidate_password) decoded by this routine
|
|
||||||
* @return Authentication status
|
|
||||||
* @note Authentication status codes are defined in maxscale/protocol/mysql.h
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
gw_check_mysql_scramble_data(DCB *dcb,
|
|
||||||
uint8_t *token,
|
|
||||||
unsigned int token_len,
|
|
||||||
uint8_t *mxs_scramble,
|
|
||||||
unsigned int scramble_len,
|
|
||||||
const char *username,
|
|
||||||
uint8_t *stage1_hash)
|
|
||||||
{
|
|
||||||
uint8_t step1[GW_MYSQL_SCRAMBLE_SIZE] = "";
|
|
||||||
uint8_t step2[GW_MYSQL_SCRAMBLE_SIZE + 1] = "";
|
|
||||||
uint8_t check_hash[GW_MYSQL_SCRAMBLE_SIZE] = "";
|
|
||||||
char hex_double_sha1[2 * GW_MYSQL_SCRAMBLE_SIZE + 1] = "";
|
|
||||||
uint8_t password[GW_MYSQL_SCRAMBLE_SIZE] = "";
|
|
||||||
/* The following can be compared using memcmp to detect a null password */
|
|
||||||
uint8_t null_client_sha1[MYSQL_SCRAMBLE_LEN] = "";
|
|
||||||
|
|
||||||
|
|
||||||
if ((username == NULL) || (mxs_scramble == NULL) || (stage1_hash == NULL))
|
|
||||||
{
|
|
||||||
return MXS_AUTH_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*<
|
|
||||||
* get the user's password from repository in SHA1(SHA1(real_password));
|
|
||||||
* please note 'real_password' is unknown!
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (gw_find_mysql_user_password_sha1(username, password, dcb))
|
|
||||||
{
|
|
||||||
/* if password was sent, fill stage1_hash with at least 1 byte in order
|
|
||||||
* to create right error message: (using password: YES|NO)
|
|
||||||
*/
|
|
||||||
if (token_len)
|
|
||||||
{
|
|
||||||
memcpy(stage1_hash, (char *)"_", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return MXS_AUTH_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token && token_len)
|
|
||||||
{
|
|
||||||
/*<
|
|
||||||
* convert in hex format: this is the content of mysql.user table.
|
|
||||||
* The field password is without the '*' prefix and it is 40 bytes long
|
|
||||||
*/
|
|
||||||
|
|
||||||
gw_bin2hex(hex_double_sha1, password, SHA_DIGEST_LENGTH);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* check if the password is not set in the user table */
|
|
||||||
return memcmp(password, null_client_sha1, MYSQL_SCRAMBLE_LEN) ?
|
|
||||||
MXS_AUTH_FAILED : MXS_AUTH_SUCCEEDED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*<
|
|
||||||
* Auth check in 3 steps
|
|
||||||
*
|
|
||||||
* Note: token = XOR (SHA1(real_password), SHA1(CONCAT(scramble, SHA1(SHA1(real_password)))))
|
|
||||||
* the client sends token
|
|
||||||
*
|
|
||||||
* Now, server side:
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* step 1: compute the STEP1 = SHA1(CONCAT(scramble, gateway_password))
|
|
||||||
* the result in step1 is SHA_DIGEST_LENGTH long
|
|
||||||
*/
|
|
||||||
|
|
||||||
gw_sha1_2_str(mxs_scramble, scramble_len, password, SHA_DIGEST_LENGTH, step1);
|
|
||||||
|
|
||||||
/*<
|
|
||||||
* step2: STEP2 = XOR(token, STEP1)
|
|
||||||
*
|
|
||||||
* token is transmitted form client and it's based on the handshake scramble and SHA1(real_passowrd)
|
|
||||||
* step1 has been computed in the previous step
|
|
||||||
* the result STEP2 is SHA1(the_password_to_check) and is SHA_DIGEST_LENGTH long
|
|
||||||
*/
|
|
||||||
|
|
||||||
gw_str_xor(step2, token, step1, token_len);
|
|
||||||
|
|
||||||
/*<
|
|
||||||
* copy the stage1_hash back to the caller
|
|
||||||
* stage1_hash will be used for backend authentication
|
|
||||||
*/
|
|
||||||
|
|
||||||
memcpy(stage1_hash, step2, SHA_DIGEST_LENGTH);
|
|
||||||
|
|
||||||
/*<
|
|
||||||
* step 3: prepare the check_hash
|
|
||||||
*
|
|
||||||
* compute the SHA1(STEP2) that is SHA1(SHA1(the_password_to_check)), and is SHA_DIGEST_LENGTH long
|
|
||||||
*/
|
|
||||||
|
|
||||||
gw_sha1_str(step2, SHA_DIGEST_LENGTH, check_hash);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef GW_DEBUG_CLIENT_AUTH
|
|
||||||
{
|
|
||||||
char inpass[128] = "";
|
|
||||||
gw_bin2hex(inpass, check_hash, SHA_DIGEST_LENGTH);
|
|
||||||
|
|
||||||
fprintf(stderr, "The CLIENT hex(SHA1(SHA1(password))) for \"%s\" is [%s]", username, inpass);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* now compare SHA1(SHA1(gateway_password)) and check_hash: return 0 is MYSQL_AUTH_OK */
|
|
||||||
return (0 == memcmp(password, check_hash, SHA_DIGEST_LENGTH)) ?
|
|
||||||
MXS_AUTH_SUCCEEDED : MXS_AUTH_FAILED;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* @brief Free the client data pointed to by the passed DCB.
|
* @brief Free the client data pointed to by the passed DCB.
|
||||||
*
|
*
|
||||||
|
@ -123,31 +123,70 @@ typedef struct mysql_user_host_key
|
|||||||
char hostname[MYSQL_HOST_MAXLEN + 1];
|
char hostname[MYSQL_HOST_MAXLEN + 1];
|
||||||
} MYSQL_USER_HOST;
|
} MYSQL_USER_HOST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add new MySQL user to the internal user database
|
||||||
|
*
|
||||||
|
* @param handle Database handle
|
||||||
|
* @param user Username
|
||||||
|
* @param host Host
|
||||||
|
* @param db Database
|
||||||
|
* @param anydb Global access to databases
|
||||||
|
*/
|
||||||
void add_mysql_user(sqlite3 *handle, const char *user, const char *host,
|
void add_mysql_user(sqlite3 *handle, const char *user, const char *host,
|
||||||
const char *db, bool anydb, const char *pw);
|
const char *db, bool anydb, const char *pw);
|
||||||
|
|
||||||
extern int add_mysql_users_with_host_ipv4(USERS *users, const char *user, const char *host,
|
/**
|
||||||
char *passwd, const char *anydb, const char *db);
|
* @brief Check if the service user has all required permissions to operate properly.
|
||||||
extern bool check_service_permissions(SERVICE* service);
|
*
|
||||||
extern bool dbusers_load(sqlite3 *handle, const char *filename);
|
* This checks for SELECT permissions on mysql.user, mysql.db and mysql.tables_priv
|
||||||
extern bool dbusers_save(sqlite3 *src, const char *filename);
|
* tables and for SHOW DATABASES permissions. If permissions are not adequate,
|
||||||
extern int mysql_users_add(USERS *users, MYSQL_USER_HOST *key, char *auth);
|
* an error message is logged and the service is not started.
|
||||||
extern USERS *mysql_users_alloc();
|
*
|
||||||
extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key);
|
* @param service Service to inspect
|
||||||
extern int replace_mysql_users(SERV_LISTENER *listener);
|
*
|
||||||
|
* @return True if service permissions are correct on at least one server, false
|
||||||
|
* if permissions are missing or if an error occurred.
|
||||||
|
*/
|
||||||
|
bool check_service_permissions(SERVICE* service);
|
||||||
|
|
||||||
int gw_check_mysql_scramble_data(DCB *dcb,
|
/**
|
||||||
uint8_t *token,
|
* Load users from persisted database
|
||||||
unsigned int token_len,
|
*
|
||||||
uint8_t *scramble,
|
* @param dest Open SQLite handle where contents are loaded
|
||||||
unsigned int scramble_len,
|
*
|
||||||
const char *username,
|
* @return True on success
|
||||||
uint8_t *stage1_hash);
|
*/
|
||||||
int check_db_name_after_auth(DCB *dcb, char *database, int auth_ret);
|
bool dbusers_load(sqlite3 *handle, const char *filename);
|
||||||
int gw_find_mysql_user_password_sha1(
|
|
||||||
const char *username,
|
/**
|
||||||
uint8_t *gateway_password,
|
* Save users to persisted database
|
||||||
DCB *dcb);
|
*
|
||||||
|
* @param dest Open SQLite handle where contents are stored
|
||||||
|
*
|
||||||
|
* @return True on success
|
||||||
|
*/
|
||||||
|
bool dbusers_save(sqlite3 *src, const char *filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload and replace the currently loaded database users
|
||||||
|
*
|
||||||
|
* @param service The current service
|
||||||
|
*
|
||||||
|
* @return -1 on any error or the number of users inserted (0 means no users at all)
|
||||||
|
*/
|
||||||
|
int replace_mysql_users(SERV_LISTENER *listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify the user has access to the database
|
||||||
|
*
|
||||||
|
* @param handle SQLite handle to MySQLAuth user database
|
||||||
|
* @param dcb Client DCB
|
||||||
|
* @param session Shared MySQL session
|
||||||
|
* @param scramble The scramble sent to the client in the initial handshake
|
||||||
|
* @param scramble_len Length of @c scramble
|
||||||
|
*
|
||||||
|
* @return True if the user has access to the database
|
||||||
|
*/
|
||||||
bool validate_mysql_user(sqlite3 *handle, DCB *dcb, MYSQL_session *session,
|
bool validate_mysql_user(sqlite3 *handle, DCB *dcb, MYSQL_session *session,
|
||||||
uint8_t *scramble, size_t scramble_len);
|
uint8_t *scramble, size_t scramble_len);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user