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:
Markus Mäkelä
2017-01-30 13:16:48 +02:00
parent b206300975
commit 04899f3a3e
3 changed files with 79 additions and 1330 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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.
* *

View File

@ -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);