Move reauthentication to authenticators

Currently the only situation where a user needs to be authenticated after
the initial authentication is when a COM_CHANGE_USER is being
executed. This was previously handled by directly calling a function in
the MySQLAuth authenticator.

The new entry in the API of the authenticators is very specific to MySQL
and should be reviewed once other protocols are added.
This commit is contained in:
Markus Mäkelä
2017-01-28 11:23:49 +02:00
parent 6da8cfe97e
commit d4a06c61de
8 changed files with 169 additions and 152 deletions

View File

@ -76,6 +76,12 @@ typedef struct mxs_authenticator
void (*free)(struct dcb *);
void (*destroy)(void *);
int (*loadusers)(struct servlistener *);
/** This entry point was added to avoid calling authenticator functions
* directly when a COM_CHANGE_USER command is executed. */
int (*reauthenticate)(struct dcb *, const char *user,
uint8_t *token, size_t token_len,
uint8_t *scramble, size_t scramble_len);
} MXS_AUTHENTICATOR;
/** Return values for extract and authenticate entry points */

View File

@ -35,7 +35,7 @@
* @endverbatim
*/
#define MXS_MODULE_NAME "MySQLAuth"
#include "mysql_auth.h"
#include <stdio.h>
#include <ctype.h>
@ -44,12 +44,10 @@
#include <maxscale/dcb.h>
#include <maxscale/service.h>
#include <maxscale/users.h>
#include "dbusers.h"
#include <maxscale/log_manager.h>
#include <maxscale/secrets.h>
#include <maxscale/protocol/mysql.h>
#include <mysqld_error.h>
#include <regex.h>
#include <maxscale/mysql_utils.h>
#include <maxscale/alloc.h>

View File

@ -1,69 +0,0 @@
#pragma once
#ifndef _MAXSCALE_DBUSERS_H
#define _MAXSCALE_DBUSERS_H
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2019-07-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
/**
* @file dbusers.h Extarct user information form the backend database
*
* @verbatim
* Revision History
*
* Date Who Description
* 25/06/13 Mark Riddoch Initial implementation
* 25/02/13 Massimiliano Pinto Added users table refresh rate default values
* 28/02/14 Massimiliano Pinto Added MySQL user and host data structure
* 03/10/14 Massimiliano Pinto Added netmask to MySQL user and host data structure
* 13/10/14 Massimiliano Pinto Added resource to MySQL user and host data structure
*
* @endverbatim
*/
#include <maxscale/cdefs.h>
#include <maxscale/service.h>
#include <maxscale/protocol/mysql.h>
#include <arpa/inet.h>
MXS_BEGIN_DECLS
/** Cache directory and file names */
static const char DBUSERS_DIR[] = "cache";
static const char DBUSERS_FILE[] = "dbusers";
/**
* MySQL user and host data structure
*/
typedef struct mysql_user_host_key
{
char *user;
struct sockaddr_in ipv4;
int netmask;
char *resource;
char hostname[MYSQL_HOST_MAXLEN + 1];
} MYSQL_USER_HOST;
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 int dbusers_load(USERS *, const char *filename);
extern int dbusers_save(USERS *, 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);
MXS_END_DECLS
#endif

View File

@ -25,25 +25,16 @@
* @endverbatim
*/
#define MXS_MODULE_NAME "MySQLAuth"
#include "mysql_auth.h"
#include <mysql_auth.h>
#include <maxscale/protocol/mysql.h>
#include <maxscale/authenticator.h>
#include <maxscale/alloc.h>
#include <maxscale/poll.h>
#include "dbusers.h"
#include <maxscale/paths.h>
#include <maxscale/secrets.h>
#include <maxscale/utils.h>
typedef struct mysql_auth
{
char *cache_dir; /**< Custom cache directory location */
bool inject_service_user; /**< Inject the service user into the list of users */
bool skip_auth; /**< Authentication will always be successful */
} MYSQL_AUTH;
static void* mysql_auth_init(char **options);
static int mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
static bool mysql_auth_is_client_ssl_capable(DCB *dcb);
@ -65,6 +56,9 @@ static int mysql_auth_set_client_data(
MySQLProtocol *protocol,
GWBUF *buffer);
int mysql_auth_reauthenticate(DCB *dcb, const char *user,
uint8_t *token, size_t token_len,
uint8_t *scramble, size_t scramble_len);
/**
* The module entry point routine. It is this routine that
* must populate the structure that is referred to as the
@ -84,7 +78,8 @@ MXS_MODULE* MXS_CREATE_MODULE()
mysql_auth_authenticate, /* Authenticate user credentials */
mysql_auth_free_client_data, /* Free the client data held in DCB */
NULL, /* No destroy entry point */
mysql_auth_load_users /* Load users from backend databases */
mysql_auth_load_users, /* Load users from backend databases */
mysql_auth_reauthenticate /* Handle COM_CHANGE_USER */
};
static MXS_MODULE info =
@ -437,7 +432,7 @@ mysql_auth_is_client_ssl_capable(DCB *dcb)
* @return 1 if user is not found or 0 if the user exists
*
*/
int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password, DCB *dcb)
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;
@ -445,7 +440,7 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password,
struct sockaddr_in *client = (struct sockaddr_in *) &dcb->ipv4;
MYSQL_USER_HOST key = {};
key.user = username;
key.user = (char*)username;
memcpy(&key.ipv4, client, sizeof(struct sockaddr_in));
key.netmask = 32;
key.resource = client_data->db;
@ -603,7 +598,7 @@ gw_check_mysql_scramble_data(DCB *dcb,
unsigned int token_len,
uint8_t *mxs_scramble,
unsigned int scramble_len,
char *username,
const char *username,
uint8_t *stage1_hash)
{
uint8_t step1[GW_MYSQL_SCRAMBLE_SIZE] = "";
@ -946,3 +941,12 @@ static int mysql_auth_load_users(SERV_LISTENER *port)
return rc;
}
int mysql_auth_reauthenticate(DCB *dcb, const char *user,
uint8_t *token, size_t token_len,
uint8_t *scramble, size_t scramble_len)
{
MYSQL_session *client_data = (MYSQL_session *)dcb->data;
return gw_check_mysql_scramble_data(dcb, token, token_len, scramble, scramble_len,
user, client_data->client_sha1);
}

View File

@ -0,0 +1,125 @@
#pragma once
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl.
*
* Change Date: 2019-07-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
/*
* @verbatim
* Revision History
*
* Date Who Description
* 02/02/2016 Martin Brampton Initial implementation
*
* @endverbatim
*/
#define MXS_MODULE_NAME "MySQLAuth"
#include <maxscale/cdefs.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <maxscale/dcb.h>
#include <maxscale/buffer.h>
#include <maxscale/service.h>
#include <maxscale/sqlite3.h>
#include <maxscale/protocol/mysql.h>
MXS_BEGIN_DECLS
/** Cache directory and file names */
static const char DBUSERS_DIR[] = "cache";
static const char DBUSERS_FILE[] = "dbusers";
#define MYSQLAUTH_DATABASE_NAME "file:mysqlauth.db"
/** The table name where we store the users */
#define MYSQLAUTH_TABLE_NAME "mysqlauth_users"
/** CREATE TABLE statement for the in-memory table */
static const char create_sql[] =
"CREATE TABLE IF NOT EXISTS " MYSQLAUTH_TABLE_NAME
"(user varchar(255), host varchar(255), db varchar(255), anydb boolean, password text)";
/** The query that is executed when a user is authenticated */
static const char mysqlauth_auth_query[] =
"SELECT * FROM " MYSQLAUTH_TABLE_NAME
" WHERE user = '%s' AND '%s' LIKE host AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db)"
" AND ('%s' = '%s') LIMIT 1";
/** Delete query used to clean up the database before loading new users */
static const char delete_query[] = "DELETE FROM " MYSQLAUTH_TABLE_NAME;
/** The insert query template which adds users to the mysqlauth_users table */
static const char insert_sql_pattern[] =
"INSERT INTO " MYSQLAUTH_TABLE_NAME " VALUES ('%s', '%s', %s, %s, %s)";
/** Used for NULL value creation in the INSERT query */
static const char null_token[] = "NULL";
/** Flags for sqlite3_open_v2() */
static int db_flags = SQLITE_OPEN_READWRITE |
SQLITE_OPEN_CREATE |
SQLITE_OPEN_URI |
SQLITE_OPEN_SHAREDCACHE;
typedef struct mysql_auth
{
sqlite3 *handle; /**< SQLite3 database handle */
char *cache_dir; /**< Custom cache directory location */
bool inject_service_user; /**< Inject the service user into the list of users */
bool skip_auth; /**< Authentication will always be successful */
} MYSQL_AUTH;
/** Common structure for both backend and client authenticators */
typedef struct gssapi_auth
{
sqlite3 *handle; /**< SQLite3 database handle */
} mysql_auth_t;
/**
* MySQL user and host data structure
*/
typedef struct mysql_user_host_key
{
char *user;
struct sockaddr_in ipv4;
int netmask;
char *resource;
char hostname[MYSQL_HOST_MAXLEN + 1];
} MYSQL_USER_HOST;
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 int dbusers_load(USERS *, const char *filename);
extern int dbusers_save(USERS *, 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);
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);
MXS_END_DECLS

View File

@ -1,50 +0,0 @@
#pragma once
#ifndef _MYSQL_AUTH_H
#define _MYSQL_AUTH_H
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2019-07-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
/*
* @verbatim
* Revision History
*
* Date Who Description
* 02/02/2016 Martin Brampton Initial implementation
*
* @endverbatim
*/
#include <maxscale/cdefs.h>
#include <maxscale/dcb.h>
#include <maxscale/buffer.h>
#include <stdint.h>
#include <maxscale/protocol/mysql.h>
MXS_BEGIN_DECLS
int gw_check_mysql_scramble_data(DCB *dcb,
uint8_t *token,
unsigned int token_len,
uint8_t *scramble,
unsigned int scramble_len,
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(
char *username,
uint8_t *gateway_password,
DCB *dcb);
MXS_END_DECLS
#endif /** _MYSQL_AUTH_H */

View File

@ -23,7 +23,6 @@
#include <maxscale/alloc.h>
#include <maxscale/modinfo.h>
#include <maxscale/protocol.h>
#include <mysql_auth.h>
/*
* MySQL Protocol module for handling the protocol between the gateway
@ -1526,11 +1525,20 @@ static int gw_change_user(DCB *backend,
* 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_dcb,
DCB *dcb = backend->session->client_dcb;
if (dcb->authfunc.reauthenticate == NULL)
{
/** Authenticator does not support reauthentication */
rv = 0;
goto retblock;
}
auth_ret = dcb->authfunc.reauthenticate(dcb, username,
auth_token, auth_token_len,
client_protocol->scramble,
sizeof(client_protocol->scramble),
username, client_sha1);
sizeof(client_protocol->scramble));
strcpy(current_session->db, current_database);
if (auth_ret != 0)
@ -1540,21 +1548,17 @@ static int gw_change_user(DCB *backend,
/* Try authentication again with new repository data */
/* Note: if no auth client authentication will fail */
*current_session->db = 0;
auth_ret = gw_check_mysql_scramble_data(
backend->session->client_dcb,
auth_ret = dcb->authfunc.reauthenticate(dcb, username,
auth_token, auth_token_len,
client_protocol->scramble,
sizeof(client_protocol->scramble),
username, client_sha1);
sizeof(client_protocol->scramble));
strcpy(current_session->db, current_database);
}
}
/* let's free the auth_token now */
if (auth_token)
{
MXS_FREE(auth_token);
}
if (auth_ret != 0)
{

View File

@ -52,7 +52,6 @@
#include <maxscale/alloc.h>
#include <maxscale/log_manager.h>
#include <maxscale/protocol/mysql.h>
#include <mysql_auth.h>
#include <maxscale/ssl.h>
#include <maxscale/poll.h>
#include <maxscale/modinfo.h>