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
|
||||
mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
{
|
||||
uint8_t *client_auth_packet = GWBUF_DATA(buf);
|
||||
MySQLProtocol *protocol = NULL;
|
||||
MYSQL_session *client_data = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -123,31 +123,70 @@ typedef struct mysql_user_host_key
|
||||
char hostname[MYSQL_HOST_MAXLEN + 1];
|
||||
} 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,
|
||||
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);
|
||||
extern bool check_service_permissions(SERVICE* service);
|
||||
extern bool dbusers_load(sqlite3 *handle, const char *filename);
|
||||
extern bool dbusers_save(sqlite3 *src, const char *filename);
|
||||
extern int mysql_users_add(USERS *users, MYSQL_USER_HOST *key, char *auth);
|
||||
extern USERS *mysql_users_alloc();
|
||||
extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key);
|
||||
extern int replace_mysql_users(SERV_LISTENER *listener);
|
||||
/**
|
||||
* @brief Check if the service user has all required permissions to operate properly.
|
||||
*
|
||||
* This checks for SELECT permissions on mysql.user, mysql.db and mysql.tables_priv
|
||||
* tables and for SHOW DATABASES permissions. If permissions are not adequate,
|
||||
* an error message is logged and the service is not started.
|
||||
*
|
||||
* @param service Service to inspect
|
||||
*
|
||||
* @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,
|
||||
unsigned int token_len,
|
||||
uint8_t *scramble,
|
||||
unsigned int scramble_len,
|
||||
const char *username,
|
||||
uint8_t *stage1_hash);
|
||||
int check_db_name_after_auth(DCB *dcb, char *database, int auth_ret);
|
||||
int gw_find_mysql_user_password_sha1(
|
||||
const char *username,
|
||||
uint8_t *gateway_password,
|
||||
DCB *dcb);
|
||||
/**
|
||||
* Load users from persisted database
|
||||
*
|
||||
* @param dest Open SQLite handle where contents are loaded
|
||||
*
|
||||
* @return True on success
|
||||
*/
|
||||
bool dbusers_load(sqlite3 *handle, const char *filename);
|
||||
|
||||
/**
|
||||
* Save users to persisted database
|
||||
*
|
||||
* @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,
|
||||
uint8_t *scramble, size_t scramble_len);
|
||||
|
||||
|
Reference in New Issue
Block a user