Uncrustify maxscale
See script directory for method. The script to run in the top level MaxScale directory is called maxscale-uncrustify.sh, which uses another script, list-src, from the same directory (so you need to set your PATH). The uncrustify version was 0.66.
This commit is contained in:
@ -47,28 +47,26 @@
|
||||
|
||||
const char CDC_USERS_FILENAME[] = "cdcusers";
|
||||
|
||||
static bool cdc_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
|
||||
static bool cdc_auth_is_client_ssl_capable(DCB *dcb);
|
||||
static int cdc_auth_authenticate(DCB *dcb);
|
||||
static void cdc_auth_free_client_data(DCB *dcb);
|
||||
static bool cdc_auth_set_protocol_data(DCB* dcb, GWBUF* buf);
|
||||
static bool cdc_auth_is_client_ssl_capable(DCB* dcb);
|
||||
static int cdc_auth_authenticate(DCB* dcb);
|
||||
static void cdc_auth_free_client_data(DCB* dcb);
|
||||
|
||||
static int cdc_set_service_user(SERV_LISTENER *listener);
|
||||
static int cdc_replace_users(SERV_LISTENER *listener);
|
||||
static int cdc_set_service_user(SERV_LISTENER* listener);
|
||||
static int cdc_replace_users(SERV_LISTENER* listener);
|
||||
|
||||
static int cdc_auth_check(
|
||||
DCB *dcb,
|
||||
CDC_protocol *protocol,
|
||||
char *username,
|
||||
uint8_t *auth_data,
|
||||
unsigned int *flags
|
||||
);
|
||||
static int cdc_auth_check(DCB* dcb,
|
||||
CDC_protocol* protocol,
|
||||
char* username,
|
||||
uint8_t* auth_data,
|
||||
unsigned int* flags
|
||||
);
|
||||
|
||||
static bool cdc_auth_set_client_data(
|
||||
CDC_session *client_data,
|
||||
CDC_protocol *protocol,
|
||||
uint8_t *client_auth_packet,
|
||||
int client_auth_packet_size
|
||||
);
|
||||
static bool cdc_auth_set_client_data(CDC_session* client_data,
|
||||
CDC_protocol* protocol,
|
||||
uint8_t* client_auth_packet,
|
||||
int client_auth_packet_size
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Add a new CDC user
|
||||
@ -79,24 +77,24 @@ static bool cdc_auth_set_client_data(
|
||||
* @param args Arguments for this command
|
||||
* @return True if user was successfully added
|
||||
*/
|
||||
static bool cdc_add_new_user(const MODULECMD_ARG *args, json_t** output)
|
||||
static bool cdc_add_new_user(const MODULECMD_ARG* args, json_t** output)
|
||||
{
|
||||
const char *user = args->argv[1].value.string;
|
||||
const char* user = args->argv[1].value.string;
|
||||
size_t userlen = strlen(user);
|
||||
const char *password = args->argv[2].value.string;
|
||||
const char* password = args->argv[2].value.string;
|
||||
uint8_t phase1[SHA_DIGEST_LENGTH];
|
||||
uint8_t phase2[SHA_DIGEST_LENGTH];
|
||||
SHA1((uint8_t*)password, strlen(password), phase1);
|
||||
SHA1(phase1, sizeof(phase1), phase2);
|
||||
|
||||
size_t data_size = userlen + 2 + SHA_DIGEST_LENGTH * 2; // Extra for the : and newline
|
||||
size_t data_size = userlen + 2 + SHA_DIGEST_LENGTH * 2; // Extra for the : and newline
|
||||
char final_data[data_size];
|
||||
strcpy(final_data, user);
|
||||
strcat(final_data, ":");
|
||||
gw_bin2hex(final_data + userlen + 1, phase2, sizeof(phase2));
|
||||
final_data[data_size - 1] = '\n';
|
||||
|
||||
SERVICE *service = args->argv[0].value.service;
|
||||
SERVICE* service = args->argv[0].value.service;
|
||||
char path[PATH_MAX + 1];
|
||||
snprintf(path, PATH_MAX, "%s/%s/", get_datadir(), service->name);
|
||||
bool rval = false;
|
||||
@ -115,7 +113,7 @@ static bool cdc_add_new_user(const MODULECMD_ARG *args, json_t** output)
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *real_err = mxs_strerror(errno);
|
||||
const char* real_err = mxs_strerror(errno);
|
||||
MXS_NOTICE("Failed to write to file '%s': %s", path, real_err);
|
||||
modulecmd_set_error("Failed to write to file '%s': %s", path, real_err);
|
||||
}
|
||||
@ -124,7 +122,7 @@ static bool cdc_add_new_user(const MODULECMD_ARG *args, json_t** output)
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *real_err = mxs_strerror(errno);
|
||||
const char* real_err = mxs_strerror(errno);
|
||||
MXS_NOTICE("Failed to open file '%s': %s", path, real_err);
|
||||
modulecmd_set_error("Failed to open file '%s': %s", path, real_err);
|
||||
}
|
||||
@ -132,7 +130,8 @@ static bool cdc_add_new_user(const MODULECMD_ARG *args, json_t** output)
|
||||
else
|
||||
{
|
||||
modulecmd_set_error("Failed to create directory '%s'. Read the MaxScale "
|
||||
"log for more details.", path);
|
||||
"log for more details.",
|
||||
path);
|
||||
}
|
||||
|
||||
return rval;
|
||||
@ -148,53 +147,56 @@ extern "C"
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static modulecmd_arg_type_t args[] =
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
{ MODULECMD_ARG_SERVICE, "Service where the user is added"},
|
||||
{ MODULECMD_ARG_STRING, "User to add"},
|
||||
{ MODULECMD_ARG_STRING, "Password of the user"}
|
||||
};
|
||||
static modulecmd_arg_type_t args[] =
|
||||
{
|
||||
{MODULECMD_ARG_SERVICE, "Service where the user is added" },
|
||||
{MODULECMD_ARG_STRING, "User to add" },
|
||||
{MODULECMD_ARG_STRING, "Password of the user" }
|
||||
};
|
||||
|
||||
modulecmd_register_command("cdc", "add_user", MODULECMD_TYPE_ACTIVE,
|
||||
cdc_add_new_user, 3, args,
|
||||
"Add a new CDC user");
|
||||
modulecmd_register_command("cdc",
|
||||
"add_user",
|
||||
MODULECMD_TYPE_ACTIVE,
|
||||
cdc_add_new_user,
|
||||
3,
|
||||
args,
|
||||
"Add a new CDC user");
|
||||
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
NULL, /* No create entry point */
|
||||
cdc_auth_set_protocol_data, /* Extract data into structure */
|
||||
cdc_auth_is_client_ssl_capable, /* Check if client supports SSL */
|
||||
cdc_auth_authenticate, /* Authenticate user credentials */
|
||||
cdc_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* No destroy entry point */
|
||||
cdc_replace_users, /* Load CDC users */
|
||||
users_default_diagnostic, /* Default diagnostic */
|
||||
users_default_diagnostic_json, /* Default diagnostic */
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
NULL, /* No create entry point */
|
||||
cdc_auth_set_protocol_data, /* Extract data into structure */
|
||||
cdc_auth_is_client_ssl_capable, /* Check if client supports SSL */
|
||||
cdc_auth_authenticate, /* Authenticate user credentials */
|
||||
cdc_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* No destroy entry point */
|
||||
cdc_replace_users, /* Load CDC users */
|
||||
users_default_diagnostic, /* Default diagnostic */
|
||||
users_default_diagnostic_json, /* Default diagnostic */
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The CDC client to MaxScale authenticator implementation",
|
||||
"V1.1.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{ { MXS_END_MODULE_PARAMS} }
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The CDC client to MaxScale authenticator implementation",
|
||||
"V1.1.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{{MXS_END_MODULE_PARAMS}}
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -207,8 +209,11 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
* @return Authentication status
|
||||
* @note Authentication status codes are defined in cdc.h
|
||||
*/
|
||||
static int cdc_auth_check(DCB *dcb, CDC_protocol *protocol, char *username,
|
||||
uint8_t *auth_data, unsigned int *flags)
|
||||
static int cdc_auth_check(DCB* dcb,
|
||||
CDC_protocol* protocol,
|
||||
char* username,
|
||||
uint8_t* auth_data,
|
||||
unsigned int* flags)
|
||||
{
|
||||
int rval = CDC_STATE_AUTH_FAILED;
|
||||
|
||||
@ -237,11 +242,10 @@ static int cdc_auth_check(DCB *dcb, CDC_protocol *protocol, char *username,
|
||||
* @return Authentication status
|
||||
* @note Authentication status codes are defined in cdc.h
|
||||
*/
|
||||
static int
|
||||
cdc_auth_authenticate(DCB *dcb)
|
||||
static int cdc_auth_authenticate(DCB* dcb)
|
||||
{
|
||||
CDC_protocol *protocol = DCB_PROTOCOL(dcb, CDC_protocol);
|
||||
CDC_session *client_data = (CDC_session *)dcb->data;
|
||||
CDC_protocol* protocol = DCB_PROTOCOL(dcb, CDC_protocol);
|
||||
CDC_session* client_data = (CDC_session*)dcb->data;
|
||||
int auth_ret;
|
||||
|
||||
if (0 == strlen(client_data->user))
|
||||
@ -253,12 +257,17 @@ cdc_auth_authenticate(DCB *dcb)
|
||||
MXS_DEBUG("Receiving connection from '%s'",
|
||||
client_data->user);
|
||||
|
||||
auth_ret = cdc_auth_check(dcb, protocol, client_data->user, client_data->auth_data, client_data->flags);
|
||||
auth_ret
|
||||
= cdc_auth_check(dcb, protocol, client_data->user, client_data->auth_data, client_data->flags);
|
||||
|
||||
/* On failed authentication try to reload users and authenticate again */
|
||||
if (CDC_STATE_AUTH_OK != auth_ret && cdc_replace_users(dcb->listener) == MXS_AUTH_LOADUSERS_OK)
|
||||
{
|
||||
auth_ret = cdc_auth_check(dcb, protocol, client_data->user, client_data->auth_data, client_data->flags);
|
||||
auth_ret = cdc_auth_check(dcb,
|
||||
protocol,
|
||||
client_data->user,
|
||||
client_data->auth_data,
|
||||
client_data->flags);
|
||||
}
|
||||
|
||||
/* on successful authentication, set user into dcb field */
|
||||
@ -270,7 +279,8 @@ cdc_auth_authenticate(DCB *dcb)
|
||||
{
|
||||
MXS_LOG_EVENT(maxscale::event::AUTHENTICATION_FAILURE,
|
||||
"%s: login attempt for user '%s', authentication failed.",
|
||||
dcb->service->name, client_data->user);
|
||||
dcb->service->name,
|
||||
client_data->user);
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,18 +301,17 @@ cdc_auth_authenticate(DCB *dcb)
|
||||
* @param buffer Pointer to pointer to buffer containing data from client
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
static bool
|
||||
cdc_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
static bool cdc_auth_set_protocol_data(DCB* dcb, GWBUF* buf)
|
||||
{
|
||||
uint8_t *client_auth_packet = GWBUF_DATA(buf);
|
||||
CDC_protocol *protocol = NULL;
|
||||
CDC_session *client_data = NULL;
|
||||
uint8_t* client_auth_packet = GWBUF_DATA(buf);
|
||||
CDC_protocol* protocol = NULL;
|
||||
CDC_session* client_data = NULL;
|
||||
int client_auth_packet_size = 0;
|
||||
|
||||
protocol = DCB_PROTOCOL(dcb, CDC_protocol);
|
||||
if (dcb->data == NULL)
|
||||
{
|
||||
if (NULL == (client_data = (CDC_session *)MXS_CALLOC(1, sizeof(CDC_session))))
|
||||
if (NULL == (client_data = (CDC_session*)MXS_CALLOC(1, sizeof(CDC_session))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -310,12 +319,14 @@ cdc_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
}
|
||||
else
|
||||
{
|
||||
client_data = (CDC_session *)dcb->data;
|
||||
client_data = (CDC_session*)dcb->data;
|
||||
}
|
||||
|
||||
client_auth_packet_size = gwbuf_length(buf);
|
||||
|
||||
return cdc_auth_set_client_data(client_data, protocol, client_auth_packet,
|
||||
return cdc_auth_set_client_data(client_data,
|
||||
protocol,
|
||||
client_auth_packet,
|
||||
client_auth_packet_size);
|
||||
}
|
||||
|
||||
@ -332,11 +343,10 @@ cdc_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
* @param client_auth_packet size An integer giving the size of the data
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
static bool
|
||||
cdc_auth_set_client_data(CDC_session *client_data,
|
||||
CDC_protocol *protocol,
|
||||
uint8_t *client_auth_packet,
|
||||
int client_auth_packet_size)
|
||||
static bool cdc_auth_set_client_data(CDC_session* client_data,
|
||||
CDC_protocol* protocol,
|
||||
uint8_t* client_auth_packet,
|
||||
int client_auth_packet_size)
|
||||
{
|
||||
if (client_auth_packet_size % 2 != 0)
|
||||
{
|
||||
@ -346,15 +356,16 @@ cdc_auth_set_client_data(CDC_session *client_data,
|
||||
|
||||
bool rval = false;
|
||||
int decoded_size = client_auth_packet_size / 2;
|
||||
char decoded_buffer[decoded_size + 1]; // Extra for terminating null
|
||||
char decoded_buffer[decoded_size + 1]; // Extra for terminating null
|
||||
|
||||
/* decode input data */
|
||||
if (client_auth_packet_size <= CDC_USER_MAXLEN)
|
||||
{
|
||||
gw_hex2bin((uint8_t*)decoded_buffer, (const char *)client_auth_packet,
|
||||
gw_hex2bin((uint8_t*)decoded_buffer,
|
||||
(const char*)client_auth_packet,
|
||||
client_auth_packet_size);
|
||||
decoded_buffer[decoded_size] = '\0';
|
||||
char *tmp_ptr = strchr(decoded_buffer, ':');
|
||||
char* tmp_ptr = strchr(decoded_buffer, ':');
|
||||
|
||||
if (tmp_ptr)
|
||||
{
|
||||
@ -378,7 +389,8 @@ cdc_auth_set_client_data(CDC_session *client_data,
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Authentication failed, client authentication packet length "
|
||||
"exceeds the maximum allowed length of %d bytes.", CDC_USER_MAXLEN);
|
||||
"exceeds the maximum allowed length of %d bytes.",
|
||||
CDC_USER_MAXLEN);
|
||||
}
|
||||
|
||||
return rval;
|
||||
@ -394,8 +406,7 @@ cdc_auth_set_client_data(CDC_session *client_data,
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Boolean indicating whether client is SSL capable
|
||||
*/
|
||||
static bool
|
||||
cdc_auth_is_client_ssl_capable(DCB *dcb)
|
||||
static bool cdc_auth_is_client_ssl_capable(DCB* dcb)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -412,8 +423,7 @@ cdc_auth_is_client_ssl_capable(DCB *dcb)
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
*/
|
||||
static void
|
||||
cdc_auth_free_client_data(DCB *dcb)
|
||||
static void cdc_auth_free_client_data(DCB* dcb)
|
||||
{
|
||||
MXS_FREE(dcb->data);
|
||||
}
|
||||
@ -425,14 +435,13 @@ cdc_auth_free_client_data(DCB *dcb)
|
||||
* @param service The current service
|
||||
* @return 0 on success, 1 on failure
|
||||
*/
|
||||
static int
|
||||
cdc_set_service_user(SERV_LISTENER *listener)
|
||||
static int cdc_set_service_user(SERV_LISTENER* listener)
|
||||
{
|
||||
SERVICE *service = listener->service;
|
||||
char *dpwd = NULL;
|
||||
char *newpasswd = NULL;
|
||||
const char *service_user = NULL;
|
||||
const char *service_passwd = NULL;
|
||||
SERVICE* service = listener->service;
|
||||
char* dpwd = NULL;
|
||||
char* newpasswd = NULL;
|
||||
const char* service_user = NULL;
|
||||
const char* service_passwd = NULL;
|
||||
|
||||
serviceGetUser(service, &service_user, &service_passwd);
|
||||
dpwd = decrypt_password(service_passwd);
|
||||
@ -477,13 +486,12 @@ cdc_set_service_user(SERV_LISTENER *listener)
|
||||
* @return -1 on error or users loaded (including 0)
|
||||
*/
|
||||
|
||||
static int
|
||||
cdc_read_users(USERS *users, char *usersfile)
|
||||
static int cdc_read_users(USERS* users, char* usersfile)
|
||||
{
|
||||
FILE *fp;
|
||||
FILE* fp;
|
||||
int loaded = 0;
|
||||
char *avro_user;
|
||||
char *user_passwd;
|
||||
char* avro_user;
|
||||
char* user_passwd;
|
||||
/* user maxlen ':' password hash '\n' '\0' */
|
||||
char read_buffer[CDC_USER_MAXLEN + 1 + SHA_DIGEST_LENGTH + 1 + 1];
|
||||
|
||||
@ -498,7 +506,7 @@ cdc_read_users(USERS *users, char *usersfile)
|
||||
{
|
||||
if (fgets(read_buffer, max_line_size, fp) != NULL)
|
||||
{
|
||||
char *tmp_ptr = read_buffer;
|
||||
char* tmp_ptr = read_buffer;
|
||||
|
||||
if ((tmp_ptr = strchr(read_buffer, ':')) != NULL)
|
||||
{
|
||||
@ -528,19 +536,23 @@ cdc_read_users(USERS *users, char *usersfile)
|
||||
*
|
||||
* @param service The current service
|
||||
*/
|
||||
int cdc_replace_users(SERV_LISTENER *listener)
|
||||
int cdc_replace_users(SERV_LISTENER* listener)
|
||||
{
|
||||
int rc = MXS_AUTH_LOADUSERS_ERROR;
|
||||
USERS *newusers = users_alloc();
|
||||
USERS* newusers = users_alloc();
|
||||
|
||||
if (newusers)
|
||||
{
|
||||
char path[PATH_MAX + 1];
|
||||
snprintf(path, PATH_MAX, "%s/%s/%s", get_datadir(), listener->service->name,
|
||||
snprintf(path,
|
||||
PATH_MAX,
|
||||
"%s/%s/%s",
|
||||
get_datadir(),
|
||||
listener->service->name,
|
||||
CDC_USERS_FILENAME);
|
||||
|
||||
int i = cdc_read_users(newusers, path);
|
||||
USERS *oldusers = NULL;
|
||||
USERS* oldusers = NULL;
|
||||
|
||||
spinlock_acquire(&listener->lock);
|
||||
|
||||
|
||||
@ -33,15 +33,15 @@
|
||||
*/
|
||||
|
||||
/** Query that gets all users that authenticate via the gssapi plugin */
|
||||
const char *gssapi_users_query =
|
||||
"SELECT u.user, u.host, d.db, u.select_priv, u.authentication_string FROM "
|
||||
"mysql.user AS u LEFT JOIN mysql.db AS d "
|
||||
"ON (u.user = d.user AND u.host = d.host) WHERE u.plugin = 'gssapi' "
|
||||
"UNION "
|
||||
"SELECT u.user, u.host, t.db, u.select_priv, u.authentication_string FROM "
|
||||
"mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
|
||||
"ON (u.user = t.user AND u.host = t.host) WHERE u.plugin = 'gssapi' "
|
||||
"ORDER BY user";
|
||||
const char* gssapi_users_query
|
||||
= "SELECT u.user, u.host, d.db, u.select_priv, u.authentication_string FROM "
|
||||
"mysql.user AS u LEFT JOIN mysql.db AS d "
|
||||
"ON (u.user = d.user AND u.host = d.host) WHERE u.plugin = 'gssapi' "
|
||||
"UNION "
|
||||
"SELECT u.user, u.host, t.db, u.select_priv, u.authentication_string FROM "
|
||||
"mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
|
||||
"ON (u.user = t.user AND u.host = t.host) WHERE u.plugin = 'gssapi' "
|
||||
"ORDER BY user";
|
||||
|
||||
#define GSSAPI_USERS_QUERY_NUM_FIELDS 5
|
||||
|
||||
@ -53,18 +53,18 @@ const char *gssapi_users_query =
|
||||
#define GSSAPI_DATABASE_NAME "file:gssapi.db?mode=memory&cache=shared"
|
||||
|
||||
/** The table name where we store the users */
|
||||
#define GSSAPI_TABLE_NAME "gssapi_users"
|
||||
#define GSSAPI_TABLE_NAME "gssapi_users"
|
||||
|
||||
/** CREATE TABLE statement for the in-memory table */
|
||||
const char create_sql[] =
|
||||
"CREATE TABLE IF NOT EXISTS " GSSAPI_TABLE_NAME
|
||||
"(user varchar(255), host varchar(255), db varchar(255), anydb boolean, princ text)";
|
||||
const char create_sql[]
|
||||
= "CREATE TABLE IF NOT EXISTS " GSSAPI_TABLE_NAME
|
||||
"(user varchar(255), host varchar(255), db varchar(255), anydb boolean, princ text)";
|
||||
|
||||
/** The query that is executed when a user is authenticated */
|
||||
static const char gssapi_auth_query[] =
|
||||
"SELECT * FROM " GSSAPI_TABLE_NAME
|
||||
" WHERE user = '%s' AND '%s' LIKE host AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db)"
|
||||
" AND ('%s' = '%s' OR princ = '%s') LIMIT 1";
|
||||
static const char gssapi_auth_query[]
|
||||
= "SELECT * FROM " GSSAPI_TABLE_NAME
|
||||
" WHERE user = '%s' AND '%s' LIKE host AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db)"
|
||||
" AND ('%s' = '%s' OR princ = '%s') LIMIT 1";
|
||||
|
||||
/** Delete query used to clean up the database before loading new users */
|
||||
static const char delete_query[] = "DELETE FROM " GSSAPI_TABLE_NAME;
|
||||
@ -75,24 +75,24 @@ static const char delete_query[] = "DELETE FROM " GSSAPI_TABLE_NAME;
|
||||
* Note that the last two values are strings that can be NULL and thus they have
|
||||
* no quoted around them. The quotes for strings are added in add_gssapi_user().
|
||||
*/
|
||||
static const char insert_sql_pattern[] =
|
||||
"INSERT INTO " GSSAPI_TABLE_NAME " VALUES ('%s', '%s', %s, %s, %s)";
|
||||
static const char insert_sql_pattern[]
|
||||
= "INSERT INTO " GSSAPI_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;
|
||||
static int db_flags = SQLITE_OPEN_READWRITE
|
||||
| SQLITE_OPEN_CREATE
|
||||
| SQLITE_OPEN_URI
|
||||
| SQLITE_OPEN_SHAREDCACHE;
|
||||
|
||||
/** The instance structure for the client side GSSAPI authenticator, created in
|
||||
* gssapi_auth_init() */
|
||||
typedef struct gssapi_instance
|
||||
{
|
||||
char *principal_name; /**< Service principal name given to the client */
|
||||
sqlite3 *handle; /**< SQLite3 database handle */
|
||||
char* principal_name;/**< Service principal name given to the client */
|
||||
sqlite3* handle; /**< SQLite3 database handle */
|
||||
} GSSAPI_INSTANCE;
|
||||
|
||||
/**
|
||||
@ -104,9 +104,9 @@ typedef struct gssapi_instance
|
||||
* @param options Listener options
|
||||
* @return Authenticator instance
|
||||
*/
|
||||
void* gssapi_auth_init(char **options)
|
||||
void* gssapi_auth_init(char** options)
|
||||
{
|
||||
GSSAPI_INSTANCE *instance = static_cast<GSSAPI_INSTANCE*>(MXS_MALLOC(sizeof(GSSAPI_INSTANCE)));
|
||||
GSSAPI_INSTANCE* instance = static_cast<GSSAPI_INSTANCE*>(MXS_MALLOC(sizeof(GSSAPI_INSTANCE)));
|
||||
|
||||
if (instance)
|
||||
{
|
||||
@ -119,7 +119,7 @@ void* gssapi_auth_init(char **options)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *err;
|
||||
char* err;
|
||||
|
||||
if (sqlite3_exec(instance->handle, create_sql, NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
@ -134,7 +134,7 @@ void* gssapi_auth_init(char **options)
|
||||
{
|
||||
if (strstr(options[i], "principal_name"))
|
||||
{
|
||||
char *ptr = strchr(options[i], '=');
|
||||
char* ptr = strchr(options[i], '=');
|
||||
if (ptr)
|
||||
{
|
||||
ptr++;
|
||||
@ -160,7 +160,7 @@ void* gssapi_auth_init(char **options)
|
||||
return instance;
|
||||
}
|
||||
|
||||
void* gssapi_auth_alloc(void *instance)
|
||||
void* gssapi_auth_alloc(void* instance)
|
||||
{
|
||||
gssapi_auth_t* rval = static_cast<gssapi_auth_t*>(MXS_MALLOC(sizeof(gssapi_auth_t)));
|
||||
|
||||
@ -186,11 +186,11 @@ void* gssapi_auth_alloc(void *instance)
|
||||
return rval;
|
||||
}
|
||||
|
||||
void gssapi_auth_free(void *data)
|
||||
void gssapi_auth_free(void* data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)data;
|
||||
gssapi_auth_t* auth = (gssapi_auth_t*)data;
|
||||
sqlite3_close_v2(auth->handle);
|
||||
MXS_FREE(auth->principal_name);
|
||||
MXS_FREE(auth);
|
||||
@ -206,25 +206,26 @@ void gssapi_auth_free(void *data)
|
||||
* GSSAPI server in order for the client to be able to request a token.
|
||||
*
|
||||
* @return Allocated packet or NULL if memory allocation failed
|
||||
* @see https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest
|
||||
* @see
|
||||
*https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest
|
||||
* @see https://web.mit.edu/kerberos/krb5-1.5/krb5-1.5.4/doc/krb5-user/What-is-a-Kerberos-Principal_003f.html
|
||||
*/
|
||||
static GWBUF* create_auth_change_packet(GSSAPI_INSTANCE *instance, gssapi_auth_t *auth)
|
||||
static GWBUF* create_auth_change_packet(GSSAPI_INSTANCE* instance, gssapi_auth_t* auth)
|
||||
{
|
||||
size_t principal_name_len = strlen(instance->principal_name);
|
||||
size_t plen = sizeof(auth_plugin_name) + 1 + principal_name_len;
|
||||
GWBUF *buffer = gwbuf_alloc(plen + MYSQL_HEADER_LEN);
|
||||
GWBUF* buffer = gwbuf_alloc(plen + MYSQL_HEADER_LEN);
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
uint8_t *data = (uint8_t*)GWBUF_DATA(buffer);
|
||||
uint8_t* data = (uint8_t*)GWBUF_DATA(buffer);
|
||||
gw_mysql_set_byte3(data, plen);
|
||||
data += 3;
|
||||
*data++ = ++auth->sequence; // Second packet
|
||||
*data++ = 0xfe; // AuthSwitchRequest command
|
||||
memcpy(data, auth_plugin_name, sizeof(auth_plugin_name)); // Plugin name
|
||||
*data++ = ++auth->sequence; // Second packet
|
||||
*data++ = 0xfe; // AuthSwitchRequest command
|
||||
memcpy(data, auth_plugin_name, sizeof(auth_plugin_name)); // Plugin name
|
||||
data += sizeof(auth_plugin_name);
|
||||
memcpy(data, instance->principal_name, principal_name_len); // Plugin data
|
||||
memcpy(data, instance->principal_name, principal_name_len); // Plugin data
|
||||
}
|
||||
|
||||
return buffer;
|
||||
@ -240,7 +241,7 @@ static GWBUF* create_auth_change_packet(GSSAPI_INSTANCE *instance, gssapi_auth_t
|
||||
* @param buffer Buffer containing the key
|
||||
* @return True on success, false if memory allocation failed
|
||||
*/
|
||||
bool store_client_token(DCB *dcb, GWBUF *buffer)
|
||||
bool store_client_token(DCB* dcb, GWBUF* buffer)
|
||||
{
|
||||
bool rval = false;
|
||||
uint8_t hdr[MYSQL_HEADER_LEN];
|
||||
@ -248,7 +249,7 @@ bool store_client_token(DCB *dcb, GWBUF *buffer)
|
||||
if (gwbuf_copy_data(buffer, 0, MYSQL_HEADER_LEN, hdr) == MYSQL_HEADER_LEN)
|
||||
{
|
||||
size_t plen = gw_mysql_get_byte3(hdr);
|
||||
MYSQL_session *ses = (MYSQL_session*)dcb->data;
|
||||
MYSQL_session* ses = (MYSQL_session*)dcb->data;
|
||||
|
||||
if ((ses->auth_token = static_cast<uint8_t*>(MXS_MALLOC(plen))))
|
||||
{
|
||||
@ -266,9 +267,9 @@ bool store_client_token(DCB *dcb, GWBUF *buffer)
|
||||
* @param dcb Client DCB
|
||||
* @param buffer Buffer containing the first authentication response
|
||||
*/
|
||||
static void copy_client_information(DCB *dcb, GWBUF *buffer)
|
||||
static void copy_client_information(DCB* dcb, GWBUF* buffer)
|
||||
{
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
gssapi_auth_t* auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
gwbuf_copy_data(buffer, MYSQL_SEQ_OFFSET, 1, &auth->sequence);
|
||||
}
|
||||
|
||||
@ -279,10 +280,10 @@ static void copy_client_information(DCB *dcb, GWBUF *buffer)
|
||||
* @param read_buffer Buffer containing the client's response
|
||||
* @return True if authentication can continue, false if not
|
||||
*/
|
||||
static bool gssapi_auth_extract(DCB *dcb, GWBUF *read_buffer)
|
||||
static bool gssapi_auth_extract(DCB* dcb, GWBUF* read_buffer)
|
||||
{
|
||||
int rval = false;
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
gssapi_auth_t* auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
|
||||
switch (auth->state)
|
||||
{
|
||||
@ -311,9 +312,9 @@ static bool gssapi_auth_extract(DCB *dcb, GWBUF *read_buffer)
|
||||
* @param dcb Client DCB
|
||||
* @return True if client supports SSL
|
||||
*/
|
||||
bool gssapi_auth_connectssl(DCB *dcb)
|
||||
bool gssapi_auth_connectssl(DCB* dcb)
|
||||
{
|
||||
MySQLProtocol *protocol = (MySQLProtocol*)dcb->protocol;
|
||||
MySQLProtocol* protocol = (MySQLProtocol*)dcb->protocol;
|
||||
return protocol->client_capabilities & GW_MYSQL_CAPABILITIES_SSL;
|
||||
}
|
||||
|
||||
@ -327,7 +328,7 @@ static gss_name_t server_name = GSS_C_NO_NAME;
|
||||
* @param output Pointer where the client principal name is stored
|
||||
* @return True if client token is valid
|
||||
*/
|
||||
static bool validate_gssapi_token(char* principal, uint8_t* token, size_t len, char **output)
|
||||
static bool validate_gssapi_token(char* principal, uint8_t* token, size_t len, char** output)
|
||||
{
|
||||
OM_uint32 major = 0, minor = 0;
|
||||
gss_buffer_desc server_buf = {0, 0};
|
||||
@ -344,9 +345,14 @@ static bool validate_gssapi_token(char* principal, uint8_t* token, size_t len, c
|
||||
return false;
|
||||
}
|
||||
|
||||
major = gss_acquire_cred(&minor, server_name, GSS_C_INDEFINITE,
|
||||
GSS_C_NO_OID_SET, GSS_C_ACCEPT,
|
||||
&credentials, NULL, NULL);
|
||||
major = gss_acquire_cred(&minor,
|
||||
server_name,
|
||||
GSS_C_INDEFINITE,
|
||||
GSS_C_NO_OID_SET,
|
||||
GSS_C_ACCEPT,
|
||||
&credentials,
|
||||
NULL,
|
||||
NULL);
|
||||
if (GSS_ERROR(major))
|
||||
{
|
||||
report_error(major, minor);
|
||||
@ -360,16 +366,23 @@ static bool validate_gssapi_token(char* principal, uint8_t* token, size_t len, c
|
||||
gss_buffer_desc in = {0, 0};
|
||||
gss_buffer_desc out = {0, 0};
|
||||
gss_buffer_desc client_name = {0, 0};
|
||||
gss_OID_desc *oid;
|
||||
gss_OID_desc* oid;
|
||||
gss_name_t client;
|
||||
|
||||
in.value = token;
|
||||
in.length = len;
|
||||
|
||||
major = gss_accept_sec_context(&minor, &handle, GSS_C_NO_CREDENTIAL,
|
||||
&in, GSS_C_NO_CHANNEL_BINDINGS,
|
||||
&client, &oid, &out,
|
||||
0, 0, NULL);
|
||||
major = gss_accept_sec_context(&minor,
|
||||
&handle,
|
||||
GSS_C_NO_CREDENTIAL,
|
||||
&in,
|
||||
GSS_C_NO_CHANNEL_BINDINGS,
|
||||
&client,
|
||||
&oid,
|
||||
&out,
|
||||
0,
|
||||
0,
|
||||
NULL);
|
||||
if (GSS_ERROR(major))
|
||||
{
|
||||
report_error(major, minor);
|
||||
@ -384,7 +397,7 @@ static bool validate_gssapi_token(char* principal, uint8_t* token, size_t len, c
|
||||
return false;
|
||||
}
|
||||
|
||||
char *princ_name = static_cast<char*>(MXS_MALLOC(client_name.length + 1));
|
||||
char* princ_name = static_cast<char*>(MXS_MALLOC(client_name.length + 1));
|
||||
|
||||
if (!princ_name)
|
||||
{
|
||||
@ -401,9 +414,9 @@ static bool validate_gssapi_token(char* principal, uint8_t* token, size_t len, c
|
||||
}
|
||||
|
||||
/** @brief Callback for sqlite3_exec() */
|
||||
static int auth_cb(void *data, int columns, char** rows, char** row_names)
|
||||
static int auth_cb(void* data, int columns, char** rows, char** row_names)
|
||||
{
|
||||
bool *rv = (bool*)data;
|
||||
bool* rv = (bool*)data;
|
||||
*rv = true;
|
||||
return 0;
|
||||
}
|
||||
@ -417,25 +430,32 @@ static int auth_cb(void *data, int columns, char** rows, char** row_names)
|
||||
* @param princ Client principal name
|
||||
* @return True if the user has access to the database
|
||||
*/
|
||||
static bool validate_user(gssapi_auth_t *auth, DCB *dcb, MYSQL_session *session, const char *princ)
|
||||
static bool validate_user(gssapi_auth_t* auth, DCB* dcb, MYSQL_session* session, const char* princ)
|
||||
{
|
||||
mxb_assert(princ);
|
||||
size_t len = sizeof(gssapi_auth_query) + strlen(session->user) * 2 +
|
||||
strlen(session->db) * 2 + strlen(dcb->remote) + strlen(princ) * 2;
|
||||
size_t len = sizeof(gssapi_auth_query) + strlen(session->user) * 2
|
||||
+ strlen(session->db) * 2 + strlen(dcb->remote) + strlen(princ) * 2;
|
||||
char sql[len + 1];
|
||||
bool rval = false;
|
||||
char *err;
|
||||
char* err;
|
||||
|
||||
char princ_user[strlen(princ) + 1];
|
||||
strcpy(princ_user, princ);
|
||||
char *at = strchr(princ_user, '@');
|
||||
char* at = strchr(princ_user, '@');
|
||||
if (at)
|
||||
{
|
||||
*at = '\0';
|
||||
}
|
||||
|
||||
sprintf(sql, gssapi_auth_query, session->user, dcb->remote, session->db,
|
||||
session->db, princ_user, session->user, princ);
|
||||
sprintf(sql,
|
||||
gssapi_auth_query,
|
||||
session->user,
|
||||
dcb->remote,
|
||||
session->db,
|
||||
session->db,
|
||||
princ_user,
|
||||
session->user,
|
||||
princ);
|
||||
|
||||
/**
|
||||
* Try authentication twice; first time with the current users, second
|
||||
@ -467,18 +487,18 @@ static bool validate_user(gssapi_auth_t *auth, DCB *dcb, MYSQL_session *session,
|
||||
* if authentication was successfully completed or MXS_AUTH_FAILED if authentication
|
||||
* has failed.
|
||||
*/
|
||||
int gssapi_auth_authenticate(DCB *dcb)
|
||||
int gssapi_auth_authenticate(DCB* dcb)
|
||||
{
|
||||
int rval = MXS_AUTH_FAILED;
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
GSSAPI_INSTANCE *instance = (GSSAPI_INSTANCE*)dcb->listener->auth_instance;
|
||||
gssapi_auth_t* auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
GSSAPI_INSTANCE* instance = (GSSAPI_INSTANCE*)dcb->listener->auth_instance;
|
||||
|
||||
if (auth->state == GSSAPI_AUTH_INIT)
|
||||
{
|
||||
/** We need to send the authentication switch packet to change the
|
||||
* authentication to something other than the 'mysql_native_password'
|
||||
* method */
|
||||
GWBUF *buffer = create_auth_change_packet(instance, auth);
|
||||
GWBUF* buffer = create_auth_change_packet(instance, auth);
|
||||
|
||||
if (buffer && dcb->func.write(dcb, buffer))
|
||||
{
|
||||
@ -491,11 +511,11 @@ int gssapi_auth_authenticate(DCB *dcb)
|
||||
/** We sent the principal name and the client responded with the GSSAPI
|
||||
* token that we must validate */
|
||||
|
||||
MYSQL_session *ses = (MYSQL_session*)dcb->data;
|
||||
char *princ = NULL;
|
||||
MYSQL_session* ses = (MYSQL_session*)dcb->data;
|
||||
char* princ = NULL;
|
||||
|
||||
if (validate_gssapi_token(instance->principal_name, ses->auth_token, ses->auth_token_len, &princ) &&
|
||||
validate_user(auth, dcb, ses, princ))
|
||||
if (validate_gssapi_token(instance->principal_name, ses->auth_token, ses->auth_token_len, &princ)
|
||||
&& validate_user(auth, dcb, ses, princ))
|
||||
{
|
||||
rval = MXS_AUTH_SUCCEEDED;
|
||||
}
|
||||
@ -511,11 +531,11 @@ int gssapi_auth_authenticate(DCB *dcb)
|
||||
*
|
||||
* @param dcb DCB to free
|
||||
*/
|
||||
void gssapi_auth_free_data(DCB *dcb)
|
||||
void gssapi_auth_free_data(DCB* dcb)
|
||||
{
|
||||
if (dcb->data)
|
||||
{
|
||||
MYSQL_session *ses = static_cast<MYSQL_session*>(dcb->data);
|
||||
MYSQL_session* ses = static_cast<MYSQL_session*>(dcb->data);
|
||||
MXS_FREE(ses->auth_token);
|
||||
MXS_FREE(ses);
|
||||
dcb->data = NULL;
|
||||
@ -526,9 +546,9 @@ void gssapi_auth_free_data(DCB *dcb)
|
||||
* @brief Delete old users from the database
|
||||
* @param handle Database handle
|
||||
*/
|
||||
static void delete_old_users(sqlite3 *handle)
|
||||
static void delete_old_users(sqlite3* handle)
|
||||
{
|
||||
char *err;
|
||||
char* err;
|
||||
|
||||
if (sqlite3_exec(handle, delete_query, NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
@ -546,10 +566,14 @@ static void delete_old_users(sqlite3 *handle)
|
||||
* @param db Database
|
||||
* @param anydb Global access to databases
|
||||
*/
|
||||
static void add_gssapi_user(sqlite3 *handle, const char *user, const char *host,
|
||||
const char *db, bool anydb, const char *princ)
|
||||
static void add_gssapi_user(sqlite3* handle,
|
||||
const char* user,
|
||||
const char* host,
|
||||
const char* db,
|
||||
bool anydb,
|
||||
const char* princ)
|
||||
{
|
||||
size_t dblen = db ? strlen(db) + 2 : sizeof(null_token); /** +2 for single quotes */
|
||||
size_t dblen = db ? strlen(db) + 2 : sizeof(null_token); /** +2 for single quotes */
|
||||
char dbstr[dblen + 1];
|
||||
|
||||
if (db)
|
||||
@ -561,7 +585,8 @@ static void add_gssapi_user(sqlite3 *handle, const char *user, const char *host,
|
||||
strcpy(dbstr, null_token);
|
||||
}
|
||||
|
||||
size_t princlen = princ && *princ ? strlen(princ) + 2 : sizeof(null_token); /** +2 for single quotes */
|
||||
size_t princlen = princ && *princ ? strlen(princ) + 2 : sizeof(null_token); /** +2 for single quotes
|
||||
* */
|
||||
char princstr[princlen + 1];
|
||||
|
||||
if (princ && *princ)
|
||||
@ -578,7 +603,7 @@ static void add_gssapi_user(sqlite3 *handle, const char *user, const char *host,
|
||||
char insert_sql[len + 1];
|
||||
sprintf(insert_sql, insert_sql_pattern, user, host, dbstr, anydb ? "1" : "0", princstr);
|
||||
|
||||
char *err;
|
||||
char* err;
|
||||
if (sqlite3_exec(handle, insert_sql, NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to insert user: %s", err);
|
||||
@ -597,12 +622,12 @@ static void add_gssapi_user(sqlite3 *handle, const char *user, const char *host,
|
||||
* @param listener Listener definition
|
||||
* @return MXS_AUTH_LOADUSERS_OK on success, MXS_AUTH_LOADUSERS_ERROR on error
|
||||
*/
|
||||
int gssapi_auth_load_users(SERV_LISTENER *listener)
|
||||
int gssapi_auth_load_users(SERV_LISTENER* listener)
|
||||
{
|
||||
const char* user;
|
||||
const char* password;
|
||||
int rval = MXS_AUTH_LOADUSERS_ERROR;
|
||||
GSSAPI_INSTANCE *inst = (GSSAPI_INSTANCE*)listener->auth_instance;
|
||||
GSSAPI_INSTANCE* inst = (GSSAPI_INSTANCE*)listener->auth_instance;
|
||||
serviceGetUser(listener->service, &user, &password);
|
||||
char* pw;
|
||||
|
||||
@ -610,7 +635,7 @@ int gssapi_auth_load_users(SERV_LISTENER *listener)
|
||||
{
|
||||
bool no_active_servers = true;
|
||||
|
||||
for (SERVER_REF *servers = listener->service->dbref; servers; servers = servers->next)
|
||||
for (SERVER_REF* servers = listener->service->dbref; servers; servers = servers->next)
|
||||
{
|
||||
if (!SERVER_REF_IS_ACTIVE(servers) || !server_is_active(servers->server))
|
||||
{
|
||||
@ -618,18 +643,19 @@ int gssapi_auth_load_users(SERV_LISTENER *listener)
|
||||
}
|
||||
|
||||
no_active_servers = false;
|
||||
MYSQL *mysql = mysql_init(NULL);
|
||||
MYSQL* mysql = mysql_init(NULL);
|
||||
|
||||
if (mxs_mysql_real_connect(mysql, servers->server, user, pw))
|
||||
{
|
||||
if (mxs_mysql_query(mysql, gssapi_users_query))
|
||||
{
|
||||
MXS_ERROR("Failed to query server '%s' for GSSAPI users: %s",
|
||||
servers->server->name, mysql_error(mysql));
|
||||
servers->server->name,
|
||||
mysql_error(mysql));
|
||||
}
|
||||
else
|
||||
{
|
||||
MYSQL_RES *res = mysql_store_result(mysql);
|
||||
MYSQL_RES* res = mysql_store_result(mysql);
|
||||
|
||||
delete_old_users(inst->handle);
|
||||
|
||||
@ -640,7 +666,10 @@ int gssapi_auth_load_users(SERV_LISTENER *listener)
|
||||
|
||||
while ((row = mysql_fetch_row(res)))
|
||||
{
|
||||
add_gssapi_user(inst->handle, row[0], row[1], row[2],
|
||||
add_gssapi_user(inst->handle,
|
||||
row[0],
|
||||
row[1],
|
||||
row[2],
|
||||
row[3] && strcasecmp(row[3], "Y") == 0,
|
||||
row[4]);
|
||||
}
|
||||
@ -675,40 +704,39 @@ extern "C"
|
||||
/**
|
||||
* Module handle entry point
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
gssapi_auth_init, /* Initialize authenticator */
|
||||
gssapi_auth_alloc, /* Allocate authenticator data */
|
||||
gssapi_auth_extract, /* Extract data into structure */
|
||||
gssapi_auth_connectssl, /* Check if client supports SSL */
|
||||
gssapi_auth_authenticate, /* Authenticate user credentials */
|
||||
gssapi_auth_free_data, /* Free the client data held in DCB */
|
||||
gssapi_auth_free, /* Free authenticator data */
|
||||
gssapi_auth_load_users, /* Load database users */
|
||||
users_default_diagnostic, /* Default user diagnostic */
|
||||
users_default_diagnostic_json, /* Default user diagnostic */
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
gssapi_auth_init, /* Initialize authenticator */
|
||||
gssapi_auth_alloc, /* Allocate authenticator data */
|
||||
gssapi_auth_extract, /* Extract data into structure */
|
||||
gssapi_auth_connectssl, /* Check if client supports SSL */
|
||||
gssapi_auth_authenticate, /* Authenticate user credentials */
|
||||
gssapi_auth_free_data, /* Free the client data held in DCB */
|
||||
gssapi_auth_free, /* Free authenticator data */
|
||||
gssapi_auth_load_users, /* Load database users */
|
||||
users_default_diagnostic, /* Default user diagnostic */
|
||||
users_default_diagnostic_json, /* Default user diagnostic */
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"GSSAPI authenticator",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{ { MXS_END_MODULE_PARAMS} }
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"GSSAPI authenticator",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{{MXS_END_MODULE_PARAMS}}
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
* @file gssapi_backend_auth.c - GSSAPI backend authenticator
|
||||
*/
|
||||
|
||||
void* gssapi_backend_auth_alloc(void *instance)
|
||||
void* gssapi_backend_auth_alloc(void* instance)
|
||||
{
|
||||
gssapi_auth_t* rval = static_cast<gssapi_auth_t*>(MXS_MALLOC(sizeof(gssapi_auth_t)));
|
||||
|
||||
@ -41,11 +41,11 @@ void* gssapi_backend_auth_alloc(void *instance)
|
||||
return rval;
|
||||
}
|
||||
|
||||
void gssapi_backend_auth_free(void *data)
|
||||
void gssapi_backend_auth_free(void* data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)data;
|
||||
gssapi_auth_t* auth = (gssapi_auth_t*)data;
|
||||
MXS_FREE(auth->principal_name);
|
||||
MXS_FREE(auth);
|
||||
}
|
||||
@ -56,7 +56,7 @@ void gssapi_backend_auth_free(void *data)
|
||||
* @param dcb Backend DCB
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
static bool send_new_auth_token(DCB *dcb)
|
||||
static bool send_new_auth_token(DCB* dcb)
|
||||
{
|
||||
bool rval = false;
|
||||
OM_uint32 major = 0, minor = 0;
|
||||
@ -65,7 +65,7 @@ static bool send_new_auth_token(DCB *dcb)
|
||||
gss_buffer_desc out = {0, 0};
|
||||
gss_buffer_desc target = {0, 0};
|
||||
gss_name_t princ = GSS_C_NO_NAME;
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
gssapi_auth_t* auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
|
||||
/** The service principal name is sent by the backend server */
|
||||
target.value = auth->principal_name;
|
||||
@ -80,9 +80,19 @@ static bool send_new_auth_token(DCB *dcb)
|
||||
}
|
||||
|
||||
/** Request the token for the service */
|
||||
major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL,
|
||||
&handle, princ, GSS_C_NO_OID, 0, 0,
|
||||
GSS_C_NO_CHANNEL_BINDINGS, &in, NULL, &out, 0, 0);
|
||||
major = gss_init_sec_context(&minor,
|
||||
GSS_C_NO_CREDENTIAL,
|
||||
&handle,
|
||||
princ,
|
||||
GSS_C_NO_OID,
|
||||
0,
|
||||
0,
|
||||
GSS_C_NO_CHANNEL_BINDINGS,
|
||||
&in,
|
||||
NULL,
|
||||
&out,
|
||||
0,
|
||||
0);
|
||||
if (GSS_ERROR(major))
|
||||
{
|
||||
report_error(major, minor);
|
||||
@ -90,11 +100,11 @@ static bool send_new_auth_token(DCB *dcb)
|
||||
else
|
||||
{
|
||||
/** We successfully requested the token, send it to the backend server */
|
||||
GWBUF *buffer = gwbuf_alloc(MYSQL_HEADER_LEN + out.length);
|
||||
GWBUF* buffer = gwbuf_alloc(MYSQL_HEADER_LEN + out.length);
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
uint8_t *data = (uint8_t*)GWBUF_DATA(buffer);
|
||||
uint8_t* data = (uint8_t*)GWBUF_DATA(buffer);
|
||||
gw_mysql_set_byte3(data, out.length);
|
||||
data += 3;
|
||||
*data++ = ++auth->sequence;
|
||||
@ -122,7 +132,6 @@ static bool send_new_auth_token(DCB *dcb)
|
||||
}
|
||||
|
||||
return rval;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,13 +141,13 @@ static bool send_new_auth_token(DCB *dcb)
|
||||
* @param buffer Buffer containing an AuthSwitchRequest packet
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
bool extract_principal_name(DCB *dcb, GWBUF *buffer)
|
||||
bool extract_principal_name(DCB* dcb, GWBUF* buffer)
|
||||
{
|
||||
bool rval = false;
|
||||
size_t buflen = gwbuf_length(buffer) - MYSQL_HEADER_LEN;
|
||||
uint8_t databuf[buflen];
|
||||
uint8_t *data = databuf;
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
uint8_t* data = databuf;
|
||||
gssapi_auth_t* auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
|
||||
/** Copy the payload and the current packet sequence number */
|
||||
gwbuf_copy_data(buffer, MYSQL_HEADER_LEN, buflen, databuf);
|
||||
@ -150,9 +159,10 @@ bool extract_principal_name(DCB *dcb, GWBUF *buffer)
|
||||
* it's possible that the server authenticated us as the anonymous user. This
|
||||
* means that the server is not secure. */
|
||||
MXS_ERROR("Server '%s' returned an unexpected authentication response.%s",
|
||||
dcb->server->name, databuf[0] == MYSQL_REPLY_OK ?
|
||||
" Authentication was complete before it even started, "
|
||||
"anonymous users might not be disabled." : "");
|
||||
dcb->server->name,
|
||||
databuf[0] == MYSQL_REPLY_OK
|
||||
? " Authentication was complete before it even started, "
|
||||
"anonymous users might not be disabled." : "");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -176,7 +186,7 @@ bool extract_principal_name(DCB *dcb, GWBUF *buffer)
|
||||
|
||||
if (buflen > 0)
|
||||
{
|
||||
uint8_t *principal = static_cast<uint8_t*>(MXS_MALLOC(buflen + 1));
|
||||
uint8_t* principal = static_cast<uint8_t*>(MXS_MALLOC(buflen + 1));
|
||||
|
||||
if (principal)
|
||||
{
|
||||
@ -204,10 +214,10 @@ bool extract_principal_name(DCB *dcb, GWBUF *buffer)
|
||||
* @return True if authentication is ongoing or complete,
|
||||
* false if authentication failed.
|
||||
*/
|
||||
static bool gssapi_backend_auth_extract(DCB *dcb, GWBUF *buffer)
|
||||
static bool gssapi_backend_auth_extract(DCB* dcb, GWBUF* buffer)
|
||||
{
|
||||
bool rval = false;
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
gssapi_auth_t* auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
|
||||
if (auth->state == GSSAPI_AUTH_INIT && extract_principal_name(dcb, buffer))
|
||||
{
|
||||
@ -231,7 +241,7 @@ static bool gssapi_backend_auth_extract(DCB *dcb, GWBUF *buffer)
|
||||
* @param dcb Backend DCB
|
||||
* @return True if DCB supports SSL
|
||||
*/
|
||||
static bool gssapi_backend_auth_connectssl(DCB *dcb)
|
||||
static bool gssapi_backend_auth_connectssl(DCB* dcb)
|
||||
{
|
||||
return dcb->server->server_ssl != NULL;
|
||||
}
|
||||
@ -242,10 +252,10 @@ static bool gssapi_backend_auth_connectssl(DCB *dcb)
|
||||
* @return MXS_AUTH_INCOMPLETE if authentication is ongoing, MXS_AUTH_SUCCEEDED
|
||||
* if authentication is complete and MXS_AUTH_FAILED if authentication failed.
|
||||
*/
|
||||
static int gssapi_backend_auth_authenticate(DCB *dcb)
|
||||
static int gssapi_backend_auth_authenticate(DCB* dcb)
|
||||
{
|
||||
int rval = MXS_AUTH_FAILED;
|
||||
gssapi_auth_t *auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
gssapi_auth_t* auth = (gssapi_auth_t*)dcb->authenticator_data;
|
||||
|
||||
if (auth->state == GSSAPI_AUTH_INIT)
|
||||
{
|
||||
@ -254,7 +264,6 @@ static int gssapi_backend_auth_authenticate(DCB *dcb)
|
||||
rval = MXS_AUTH_INCOMPLETE;
|
||||
auth->state = GSSAPI_AUTH_DATA_SENT;
|
||||
}
|
||||
|
||||
}
|
||||
else if (auth->state == GSSAPI_AUTH_OK)
|
||||
{
|
||||
@ -269,40 +278,39 @@ extern "C"
|
||||
/**
|
||||
* Module handle entry point
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
gssapi_backend_auth_alloc, /* Allocate authenticator data */
|
||||
gssapi_backend_auth_extract, /* Extract data into structure */
|
||||
gssapi_backend_auth_connectssl, /* Check if client supports SSL */
|
||||
gssapi_backend_auth_authenticate, /* Authenticate user credentials */
|
||||
NULL, /* Client plugin will free shared data */
|
||||
gssapi_backend_auth_free, /* Free authenticator data */
|
||||
NULL, /* Load users from backend databases */
|
||||
NULL, /* No diagnostic */
|
||||
NULL,
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
gssapi_backend_auth_alloc, /* Allocate authenticator data */
|
||||
gssapi_backend_auth_extract, /* Extract data into structure */
|
||||
gssapi_backend_auth_connectssl, /* Check if client supports SSL */
|
||||
gssapi_backend_auth_authenticate, /* Authenticate user credentials */
|
||||
NULL, /* Client plugin will free shared data */
|
||||
gssapi_backend_auth_free, /* Free authenticator data */
|
||||
NULL, /* Load users from backend databases */
|
||||
NULL, /* No diagnostic */
|
||||
NULL,
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"GSSAPI backend authenticator",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{ { MXS_END_MODULE_PARAMS} }
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"GSSAPI backend authenticator",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{{MXS_END_MODULE_PARAMS}}
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,11 +40,11 @@ enum gssapi_auth_state
|
||||
/** Common structure for both backend and client authenticators */
|
||||
typedef struct gssapi_auth
|
||||
{
|
||||
enum gssapi_auth_state state; /**< Authentication state*/
|
||||
uint8_t *principal_name; /**< Principal name */
|
||||
size_t principal_name_len; /**< Length of the principal name */
|
||||
uint8_t sequence; /**< The next packet seqence number */
|
||||
sqlite3 *handle; /**< SQLite3 database handle */
|
||||
enum gssapi_auth_state state; /**< Authentication state*/
|
||||
uint8_t* principal_name; /**< Principal name */
|
||||
size_t principal_name_len; /**< Length of the principal name */
|
||||
uint8_t sequence; /**< The next packet seqence number */
|
||||
sqlite3* handle; /**< SQLite3 database handle */
|
||||
} gssapi_auth_t;
|
||||
|
||||
/** Report GSSAPI errors */
|
||||
|
||||
@ -36,10 +36,10 @@
|
||||
#include <maxscale/secrets.h>
|
||||
#include <maxscale/users.h>
|
||||
|
||||
static bool http_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
|
||||
static bool http_auth_is_client_ssl_capable(DCB *dcb);
|
||||
static int http_auth_authenticate(DCB *dcb);
|
||||
static void http_auth_free_client_data(DCB *dcb);
|
||||
static bool http_auth_set_protocol_data(DCB* dcb, GWBUF* buf);
|
||||
static bool http_auth_is_client_ssl_capable(DCB* dcb);
|
||||
static int http_auth_authenticate(DCB* dcb);
|
||||
static void http_auth_free_client_data(DCB* dcb);
|
||||
|
||||
typedef struct http_auth
|
||||
{
|
||||
@ -57,41 +57,41 @@ extern "C"
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
NULL, /* No create entry point */
|
||||
http_auth_set_protocol_data, /* Extract data into structure */
|
||||
http_auth_is_client_ssl_capable, /* Check if client supports SSL */
|
||||
http_auth_authenticate, /* Authenticate user credentials */
|
||||
http_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers, /* Load generic users */
|
||||
users_default_diagnostic, /* Default user diagnostic */
|
||||
users_default_diagnostic_json, /* Default user diagnostic */
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
NULL, /* No create entry point */
|
||||
http_auth_set_protocol_data, /* Extract data into structure */
|
||||
http_auth_is_client_ssl_capable,/* Check if client supports SSL */
|
||||
http_auth_authenticate, /* Authenticate user credentials */
|
||||
http_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers, /* Load generic users */
|
||||
users_default_diagnostic, /* Default user diagnostic */
|
||||
users_default_diagnostic_json, /* Default user diagnostic */
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The MaxScale HTTP BA authenticator",
|
||||
"V1.1.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{ { MXS_END_MODULE_PARAMS} }
|
||||
};
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The MaxScale HTTP BA authenticator",
|
||||
"V1.1.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{{MXS_END_MODULE_PARAMS}}
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
return &info;
|
||||
}
|
||||
/*lint +e14 */
|
||||
}
|
||||
|
||||
@ -103,11 +103,10 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Authentication status - always 0 to denote success
|
||||
*/
|
||||
static int
|
||||
http_auth_authenticate(DCB *dcb)
|
||||
static int http_auth_authenticate(DCB* dcb)
|
||||
{
|
||||
int rval = 1;
|
||||
HTTP_AUTH *ses = (HTTP_AUTH*)dcb->data;
|
||||
HTTP_AUTH* ses = (HTTP_AUTH*)dcb->data;
|
||||
const char* user;
|
||||
const char* password;
|
||||
|
||||
@ -134,8 +133,7 @@ http_auth_authenticate(DCB *dcb)
|
||||
* @param buffer Pointer to pointer to buffers containing data from client
|
||||
* @return Authentication status - true for success, false for failure
|
||||
*/
|
||||
static bool
|
||||
http_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
static bool http_auth_set_protocol_data(DCB* dcb, GWBUF* buf)
|
||||
{
|
||||
bool rval = false;
|
||||
char* value = (char*)GWBUF_DATA(buf);
|
||||
@ -146,19 +144,19 @@ http_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
tok++;
|
||||
char outbuf[strlen(tok) * 2 + 1];
|
||||
|
||||
BIO *b64 = BIO_new(BIO_f_base64());
|
||||
BIO* b64 = BIO_new(BIO_f_base64());
|
||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
|
||||
BIO *bio = BIO_new_mem_buf(tok, -1);
|
||||
BIO* bio = BIO_new_mem_buf(tok, -1);
|
||||
BIO_push(b64, bio);
|
||||
int nread = BIO_read(b64, outbuf, sizeof(outbuf));
|
||||
outbuf[nread] = '\0';
|
||||
BIO_free_all(b64);
|
||||
char *pw_start = strchr(outbuf, ':');
|
||||
char* pw_start = strchr(outbuf, ':');
|
||||
if (pw_start)
|
||||
{
|
||||
*pw_start++ = '\0';
|
||||
HTTP_AUTH *ses = static_cast<HTTP_AUTH*>(MXS_MALLOC(sizeof(*ses)));
|
||||
HTTP_AUTH* ses = static_cast<HTTP_AUTH*>(MXS_MALLOC(sizeof(*ses)));
|
||||
char* user = MXS_STRDUP(outbuf);
|
||||
char* pw = MXS_STRDUP(pw_start);
|
||||
|
||||
@ -190,8 +188,7 @@ http_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Boolean indicating whether client is SSL capable - false
|
||||
*/
|
||||
static bool
|
||||
http_auth_is_client_ssl_capable(DCB *dcb)
|
||||
static bool http_auth_is_client_ssl_capable(DCB* dcb)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -204,10 +201,9 @@ http_auth_is_client_ssl_capable(DCB *dcb)
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
*/
|
||||
static void
|
||||
http_auth_free_client_data(DCB *dcb)
|
||||
static void http_auth_free_client_data(DCB* dcb)
|
||||
{
|
||||
HTTP_AUTH *ses = (HTTP_AUTH*)dcb->data;
|
||||
HTTP_AUTH* ses = (HTTP_AUTH*)dcb->data;
|
||||
MXS_FREE(ses->user);
|
||||
MXS_FREE(ses->pw);
|
||||
MXS_FREE(ses);
|
||||
|
||||
@ -36,10 +36,10 @@
|
||||
#include <maxscale/adminusers.h>
|
||||
#include <maxscale/users.h>
|
||||
|
||||
static bool max_admin_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
|
||||
static bool max_admin_auth_is_client_ssl_capable(DCB *dcb);
|
||||
static int max_admin_auth_authenticate(DCB *dcb);
|
||||
static void max_admin_auth_free_client_data(DCB *dcb);
|
||||
static bool max_admin_auth_set_protocol_data(DCB* dcb, GWBUF* buf);
|
||||
static bool max_admin_auth_is_client_ssl_capable(DCB* dcb);
|
||||
static int max_admin_auth_authenticate(DCB* dcb);
|
||||
static void max_admin_auth_free_client_data(DCB* dcb);
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@ -51,41 +51,41 @@ extern "C"
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
NULL, /* No create entry point */
|
||||
max_admin_auth_set_protocol_data, /* Extract data into structure */
|
||||
max_admin_auth_is_client_ssl_capable, /* Check if client supports SSL */
|
||||
max_admin_auth_authenticate, /* Authenticate user credentials */
|
||||
max_admin_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers, /* Load generic users */
|
||||
users_default_diagnostic, /* Default user diagnostic */
|
||||
users_default_diagnostic_json, /* Default user diagnostic */
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
NULL, /* No create entry point */
|
||||
max_admin_auth_set_protocol_data, /* Extract data into structure */
|
||||
max_admin_auth_is_client_ssl_capable, /* Check if client supports SSL */
|
||||
max_admin_auth_authenticate, /* Authenticate user credentials */
|
||||
max_admin_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers, /* Load generic users */
|
||||
users_default_diagnostic, /* Default user diagnostic */
|
||||
users_default_diagnostic_json, /* Default user diagnostic */
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The MaxScale Admin client authenticator implementation",
|
||||
"V2.1.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{ { MXS_END_MODULE_PARAMS} }
|
||||
};
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The MaxScale Admin client authenticator implementation",
|
||||
"V2.1.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{{MXS_END_MODULE_PARAMS}}
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
return &info;
|
||||
}
|
||||
/*lint +e14 */
|
||||
}
|
||||
|
||||
@ -97,10 +97,9 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Authentication status - always 0 to denote success
|
||||
*/
|
||||
static int
|
||||
max_admin_auth_authenticate(DCB *dcb)
|
||||
static int max_admin_auth_authenticate(DCB* dcb)
|
||||
{
|
||||
return (dcb->data != NULL && ((ADMIN_session *)dcb->data)->validated) ? 0 : 1;
|
||||
return (dcb->data != NULL && ((ADMIN_session*)dcb->data)->validated) ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,19 +112,18 @@ max_admin_auth_authenticate(DCB *dcb)
|
||||
* @param buffer Pointer to pointer to buffers containing data from client
|
||||
* @return Authentication status - true for success, false for failure
|
||||
*/
|
||||
static bool
|
||||
max_admin_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
static bool max_admin_auth_set_protocol_data(DCB* dcb, GWBUF* buf)
|
||||
{
|
||||
ADMIN_session *session_data;
|
||||
ADMIN_session* session_data;
|
||||
|
||||
max_admin_auth_free_client_data(dcb);
|
||||
|
||||
if ((session_data = (ADMIN_session *)MXS_CALLOC(1, sizeof(ADMIN_session))) != NULL)
|
||||
if ((session_data = (ADMIN_session*)MXS_CALLOC(1, sizeof(ADMIN_session))) != NULL)
|
||||
{
|
||||
int user_len = (GWBUF_LENGTH(buf) > ADMIN_USER_MAXLEN) ? ADMIN_USER_MAXLEN : GWBUF_LENGTH(buf);
|
||||
memcpy(session_data->user, GWBUF_DATA(buf), user_len);
|
||||
session_data->validated = false;
|
||||
dcb->data = (void *)session_data;
|
||||
dcb->data = (void*)session_data;
|
||||
|
||||
/* Check for existance of the user */
|
||||
if (admin_linux_account_enabled(session_data->user))
|
||||
@ -146,8 +144,7 @@ max_admin_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Boolean indicating whether client is SSL capable - false
|
||||
*/
|
||||
static bool
|
||||
max_admin_auth_is_client_ssl_capable(DCB *dcb)
|
||||
static bool max_admin_auth_is_client_ssl_capable(DCB* dcb)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -160,8 +157,7 @@ max_admin_auth_is_client_ssl_capable(DCB *dcb)
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
*/
|
||||
static void
|
||||
max_admin_auth_free_client_data(DCB *dcb)
|
||||
static void max_admin_auth_free_client_data(DCB* dcb)
|
||||
{
|
||||
MXS_FREE(dcb->data);
|
||||
}
|
||||
|
||||
@ -43,7 +43,8 @@
|
||||
/** MySQL 5.7 password column name */
|
||||
#define MYSQL57_PASSWORD "authentication_string"
|
||||
|
||||
#define NEW_LOAD_DBUSERS_QUERY "SELECT u.user, u.host, d.db, u.select_priv, u.%s \
|
||||
#define NEW_LOAD_DBUSERS_QUERY \
|
||||
"SELECT u.user, u.host, d.db, u.select_priv, u.%s \
|
||||
FROM mysql.user AS u LEFT JOIN mysql.db AS d \
|
||||
ON (u.user = d.user AND u.host = d.host) WHERE u.plugin IN ('', 'mysql_native_password') %s \
|
||||
UNION \
|
||||
@ -51,83 +52,83 @@
|
||||
FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t \
|
||||
ON (u.user = t.user AND u.host = t.host) WHERE u.plugin IN ('', 'mysql_native_password') %s"
|
||||
|
||||
// Query used with MariaDB 10.1 and newer, supports roles
|
||||
const char* mariadb_users_query =
|
||||
// First, select all users
|
||||
"SELECT t.user, t.host, t.db, t.select_priv, t.password FROM "
|
||||
"( "
|
||||
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
|
||||
" ON (u.user = d.user AND u.host = d.host) "
|
||||
" UNION "
|
||||
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.is_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
|
||||
" ON (u.user = t.user AND u.host = t.host) "
|
||||
") AS t "
|
||||
// Discard any users that are roles
|
||||
"WHERE t.is_role <> 'Y' %s "
|
||||
"UNION "
|
||||
// Then select all users again
|
||||
"SELECT r.user, r.host, u.db, u.select_priv, t.password FROM "
|
||||
"( "
|
||||
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.default_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
|
||||
" ON (u.user = d.user AND u.host = d.host) "
|
||||
" UNION "
|
||||
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.default_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
|
||||
" ON (u.user = t.user AND u.host = t.host) "
|
||||
") AS t "
|
||||
// Join it to the roles_mapping table to only have users with roles
|
||||
"JOIN mysql.roles_mapping AS r "
|
||||
"ON (r.user = t.user AND r.host = t.host) "
|
||||
// Then join it into itself to get the privileges of the role with the name of the user
|
||||
"JOIN "
|
||||
"( "
|
||||
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
|
||||
" ON (u.user = d.user AND u.host = d.host) "
|
||||
" UNION "
|
||||
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.is_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
|
||||
" ON (u.user = t.user AND u.host = t.host) "
|
||||
") AS u "
|
||||
"ON (u.user = r.role AND u.is_role = 'Y') "
|
||||
// We only care about users that have a default role assigned
|
||||
"WHERE t.default_role = u.user %s;";
|
||||
// Query used with MariaDB 10.1 and newer, supports roles
|
||||
const char* mariadb_users_query
|
||||
= // First, select all users
|
||||
"SELECT t.user, t.host, t.db, t.select_priv, t.password FROM "
|
||||
"( "
|
||||
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
|
||||
" ON (u.user = d.user AND u.host = d.host) "
|
||||
" UNION "
|
||||
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.is_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
|
||||
" ON (u.user = t.user AND u.host = t.host) "
|
||||
") AS t "
|
||||
// Discard any users that are roles
|
||||
"WHERE t.is_role <> 'Y' %s "
|
||||
"UNION "
|
||||
// Then select all users again
|
||||
"SELECT r.user, r.host, u.db, u.select_priv, t.password FROM "
|
||||
"( "
|
||||
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.default_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
|
||||
" ON (u.user = d.user AND u.host = d.host) "
|
||||
" UNION "
|
||||
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.default_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
|
||||
" ON (u.user = t.user AND u.host = t.host) "
|
||||
") AS t "
|
||||
// Join it to the roles_mapping table to only have users with roles
|
||||
"JOIN mysql.roles_mapping AS r "
|
||||
"ON (r.user = t.user AND r.host = t.host) "
|
||||
// Then join it into itself to get the privileges of the role with the name of the user
|
||||
"JOIN "
|
||||
"( "
|
||||
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.db AS d "
|
||||
" ON (u.user = d.user AND u.host = d.host) "
|
||||
" UNION "
|
||||
" SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.is_role "
|
||||
" FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t "
|
||||
" ON (u.user = t.user AND u.host = t.host) "
|
||||
") AS u "
|
||||
"ON (u.user = r.role AND u.is_role = 'Y') "
|
||||
// We only care about users that have a default role assigned
|
||||
"WHERE t.default_role = u.user %s;";
|
||||
|
||||
static int get_users(SERV_LISTENER *listener, bool skip_local);
|
||||
static MYSQL *gw_mysql_init(void);
|
||||
static int gw_mysql_set_timeouts(MYSQL* handle);
|
||||
static char *mysql_format_user_entry(void *data);
|
||||
static bool get_hostname(DCB *dcb, char *client_hostname, size_t size);
|
||||
static int get_users(SERV_LISTENER* listener, bool skip_local);
|
||||
static MYSQL* gw_mysql_init(void);
|
||||
static int gw_mysql_set_timeouts(MYSQL* handle);
|
||||
static char* mysql_format_user_entry(void* data);
|
||||
static bool get_hostname(DCB* dcb, char* client_hostname, size_t size);
|
||||
|
||||
static char* get_mariadb_users_query(bool include_root)
|
||||
{
|
||||
const char *root = include_root ? "" : " AND t.user NOT IN ('root')";
|
||||
const char* root = include_root ? "" : " AND t.user NOT IN ('root')";
|
||||
|
||||
size_t n_bytes = snprintf(NULL, 0, mariadb_users_query, root, root);
|
||||
char *rval = static_cast<char*>(MXS_MALLOC(n_bytes + 1));
|
||||
char* rval = static_cast<char*>(MXS_MALLOC(n_bytes + 1));
|
||||
MXS_ABORT_IF_NULL(rval);
|
||||
snprintf(rval, n_bytes + 1, mariadb_users_query, root, root);
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
static char* get_users_query(const char *server_version, bool include_root, bool is_mariadb)
|
||||
static char* get_users_query(const char* server_version, bool include_root, bool is_mariadb)
|
||||
{
|
||||
if (is_mariadb) // 10.1.1 or newer, supports default roles
|
||||
if (is_mariadb) // 10.1.1 or newer, supports default roles
|
||||
{
|
||||
return get_mariadb_users_query(include_root);
|
||||
}
|
||||
|
||||
// Either an older MariaDB version or a MySQL variant, use the legacy query
|
||||
const char* password = strstr(server_version, "5.7.") || strstr(server_version, "8.0.")
|
||||
? MYSQL57_PASSWORD : MYSQL_PASSWORD;
|
||||
const char *with_root = include_root ? "" : " AND u.user NOT IN ('root')";
|
||||
? MYSQL57_PASSWORD : MYSQL_PASSWORD;
|
||||
const char* with_root = include_root ? "" : " AND u.user NOT IN ('root')";
|
||||
|
||||
size_t n_bytes = snprintf(NULL, 0, NEW_LOAD_DBUSERS_QUERY, password, with_root, password, with_root);
|
||||
char *rval = static_cast<char*>(MXS_MALLOC(n_bytes + 1));
|
||||
char* rval = static_cast<char*>(MXS_MALLOC(n_bytes + 1));
|
||||
|
||||
if (rval)
|
||||
{
|
||||
@ -137,14 +138,18 @@ static char* get_users_query(const char *server_version, bool include_root, bool
|
||||
return rval;
|
||||
}
|
||||
|
||||
int replace_mysql_users(SERV_LISTENER *listener, bool skip_local)
|
||||
int replace_mysql_users(SERV_LISTENER* listener, bool skip_local)
|
||||
{
|
||||
int i = get_users(listener, skip_local);
|
||||
return i;
|
||||
}
|
||||
|
||||
static bool check_password(const char *output, uint8_t *token, size_t token_len,
|
||||
uint8_t *scramble, size_t scramble_len, uint8_t *phase2_scramble)
|
||||
static bool check_password(const char* output,
|
||||
uint8_t* token,
|
||||
size_t token_len,
|
||||
uint8_t* scramble,
|
||||
size_t scramble_len,
|
||||
uint8_t* phase2_scramble)
|
||||
{
|
||||
uint8_t stored_token[SHA_DIGEST_LENGTH] = {};
|
||||
size_t stored_token_len = sizeof(stored_token);
|
||||
@ -192,14 +197,14 @@ static bool check_password(const char *output, uint8_t *token, size_t token_len,
|
||||
}
|
||||
|
||||
/** Callback for check_database() */
|
||||
static int database_cb(void *data, int columns, char** rows, char** row_names)
|
||||
static int database_cb(void* data, int columns, char** rows, char** row_names)
|
||||
{
|
||||
bool *rval = (bool*)data;
|
||||
bool* rval = (bool*)data;
|
||||
*rval = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool check_database(sqlite3 *handle, const char *database)
|
||||
static bool check_database(sqlite3* handle, const char* database)
|
||||
{
|
||||
bool rval = true;
|
||||
|
||||
@ -211,7 +216,7 @@ static bool check_database(sqlite3 *handle, const char *database)
|
||||
|
||||
sprintf(sql, mysqlauth_validate_database_query, database);
|
||||
|
||||
char *err;
|
||||
char* err;
|
||||
|
||||
if (sqlite3_exec(handle, sql, database_cb, &rval, &err) != SQLITE_OK)
|
||||
{
|
||||
@ -224,7 +229,7 @@ static bool check_database(sqlite3 *handle, const char *database)
|
||||
return rval;
|
||||
}
|
||||
|
||||
static bool no_password_required(const char *result, size_t tok_len)
|
||||
static bool no_password_required(const char* result, size_t tok_len)
|
||||
{
|
||||
return *result == '\0' && tok_len == 0;
|
||||
}
|
||||
@ -237,26 +242,29 @@ struct user_query_result
|
||||
};
|
||||
|
||||
/** @brief Callback for sqlite3_exec() */
|
||||
static int auth_cb(void *data, int columns, char** rows, char** row_names)
|
||||
static int auth_cb(void* data, int columns, char** rows, char** row_names)
|
||||
{
|
||||
struct user_query_result *res = (struct user_query_result*)data;
|
||||
struct user_query_result* res = (struct user_query_result*)data;
|
||||
strcpy(res->output, rows[0] ? rows[0] : "");
|
||||
res->ok = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int validate_mysql_user(MYSQL_AUTH* instance, DCB *dcb, MYSQL_session *session,
|
||||
uint8_t *scramble, size_t scramble_len)
|
||||
int validate_mysql_user(MYSQL_AUTH* instance,
|
||||
DCB* dcb,
|
||||
MYSQL_session* session,
|
||||
uint8_t* scramble,
|
||||
size_t scramble_len)
|
||||
{
|
||||
sqlite3 *handle = get_handle(instance);
|
||||
const char* validate_query = instance->lower_case_table_names ?
|
||||
mysqlauth_validate_user_query_lower :
|
||||
mysqlauth_validate_user_query;
|
||||
size_t len = strlen(validate_query) + 1 + strlen(session->user) * 2 +
|
||||
strlen(session->db) * 2 + MYSQL_HOST_MAXLEN + session->auth_token_len * 4 + 1;
|
||||
sqlite3* handle = get_handle(instance);
|
||||
const char* validate_query = instance->lower_case_table_names
|
||||
? mysqlauth_validate_user_query_lower
|
||||
: mysqlauth_validate_user_query;
|
||||
size_t len = strlen(validate_query) + 1 + strlen(session->user) * 2
|
||||
+ strlen(session->db) * 2 + MYSQL_HOST_MAXLEN + session->auth_token_len * 4 + 1;
|
||||
char sql[len + 1];
|
||||
int rval = MXS_AUTH_FAILED;
|
||||
char *err;
|
||||
char* err;
|
||||
|
||||
if (instance->skip_auth)
|
||||
{
|
||||
@ -264,8 +272,13 @@ int validate_mysql_user(MYSQL_AUTH* instance, DCB *dcb, MYSQL_session *session,
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(sql, validate_query, session->user, dcb->remote,
|
||||
dcb->remote, session->db, session->db);
|
||||
sprintf(sql,
|
||||
validate_query,
|
||||
session->user,
|
||||
dcb->remote,
|
||||
dcb->remote,
|
||||
session->db,
|
||||
session->db);
|
||||
}
|
||||
|
||||
struct user_query_result res = {};
|
||||
@ -279,9 +292,14 @@ int validate_mysql_user(MYSQL_AUTH* instance, DCB *dcb, MYSQL_session *session,
|
||||
/** Check for IPv6 mapped IPv4 address */
|
||||
if (!res.ok && strchr(dcb->remote, ':') && strchr(dcb->remote, '.'))
|
||||
{
|
||||
const char *ipv4 = strrchr(dcb->remote, ':') + 1;
|
||||
sprintf(sql, validate_query, session->user, ipv4, ipv4,
|
||||
session->db, session->db);
|
||||
const char* ipv4 = strrchr(dcb->remote, ':') + 1;
|
||||
sprintf(sql,
|
||||
validate_query,
|
||||
session->user,
|
||||
ipv4,
|
||||
ipv4,
|
||||
session->db,
|
||||
session->db);
|
||||
|
||||
if (sqlite3_exec(handle, sql, auth_cb, &res, &err) != SQLITE_OK)
|
||||
{
|
||||
@ -299,8 +317,13 @@ int validate_mysql_user(MYSQL_AUTH* instance, DCB *dcb, MYSQL_session *session,
|
||||
char client_hostname[MYSQL_HOST_MAXLEN] = "";
|
||||
get_hostname(dcb, client_hostname, sizeof(client_hostname) - 1);
|
||||
|
||||
sprintf(sql, validate_query, session->user, client_hostname,
|
||||
client_hostname, session->db, session->db);
|
||||
sprintf(sql,
|
||||
validate_query,
|
||||
session->user,
|
||||
client_hostname,
|
||||
client_hostname,
|
||||
session->db,
|
||||
session->db);
|
||||
|
||||
if (sqlite3_exec(handle, sql, auth_cb, &res, &err) != SQLITE_OK)
|
||||
{
|
||||
@ -313,9 +336,13 @@ int validate_mysql_user(MYSQL_AUTH* instance, DCB *dcb, MYSQL_session *session,
|
||||
{
|
||||
/** Found a matching row */
|
||||
|
||||
if (no_password_required(res.output, session->auth_token_len) ||
|
||||
check_password(res.output, session->auth_token, session->auth_token_len,
|
||||
scramble, scramble_len, session->client_sha1))
|
||||
if (no_password_required(res.output, session->auth_token_len)
|
||||
|| check_password(res.output,
|
||||
session->auth_token,
|
||||
session->auth_token_len,
|
||||
scramble,
|
||||
scramble_len,
|
||||
session->client_sha1))
|
||||
{
|
||||
/** Password is OK, check that the database exists */
|
||||
if (check_database(handle, session->db))
|
||||
@ -337,13 +364,13 @@ int validate_mysql_user(MYSQL_AUTH* instance, DCB *dcb, MYSQL_session *session,
|
||||
*
|
||||
* @param handle SQLite handle
|
||||
*/
|
||||
static bool delete_mysql_users(sqlite3 *handle)
|
||||
static bool delete_mysql_users(sqlite3* handle)
|
||||
{
|
||||
bool rval = true;
|
||||
char *err;
|
||||
char* err;
|
||||
|
||||
if (sqlite3_exec(handle, delete_users_query, NULL, NULL, &err) != SQLITE_OK ||
|
||||
sqlite3_exec(handle, delete_databases_query, NULL, NULL, &err) != SQLITE_OK)
|
||||
if (sqlite3_exec(handle, delete_users_query, NULL, NULL, &err) != SQLITE_OK
|
||||
|| sqlite3_exec(handle, delete_databases_query, NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to delete old users: %s", err);
|
||||
sqlite3_free(err);
|
||||
@ -363,20 +390,20 @@ static bool delete_mysql_users(sqlite3 *handle)
|
||||
* @param host The hostname, which is modified in-place. If merging is unsuccessful,
|
||||
* it may end up garbled.
|
||||
*/
|
||||
static void merge_netmask(char *host)
|
||||
static void merge_netmask(char* host)
|
||||
{
|
||||
char *delimiter_loc = strchr(host, '/');
|
||||
char* delimiter_loc = strchr(host, '/');
|
||||
if (delimiter_loc == NULL)
|
||||
{
|
||||
return; // Nothing to do
|
||||
return; // Nothing to do
|
||||
}
|
||||
/* If anything goes wrong, we put the '/' back in to ensure the hostname
|
||||
* cannot be used.
|
||||
*/
|
||||
*delimiter_loc = '\0';
|
||||
|
||||
char *ip_token_loc = host;
|
||||
char *mask_token_loc = delimiter_loc + 1; // This is at minimum a \0
|
||||
char* ip_token_loc = host;
|
||||
char* mask_token_loc = delimiter_loc + 1; // This is at minimum a \0
|
||||
|
||||
while (ip_token_loc && mask_token_loc)
|
||||
{
|
||||
@ -396,7 +423,8 @@ static void merge_netmask(char *host)
|
||||
*/
|
||||
*delimiter_loc = '/';
|
||||
MXS_ERROR("Unrecognized IP-bytes in host/mask-combination. "
|
||||
"Merge incomplete: %s", host);
|
||||
"Merge incomplete: %s",
|
||||
host);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -412,14 +440,19 @@ static void merge_netmask(char *host)
|
||||
{
|
||||
*delimiter_loc = '/';
|
||||
MXS_ERROR("Unequal number of IP-bytes in host/mask-combination. "
|
||||
"Merge incomplete: %s", host);
|
||||
"Merge incomplete: %s",
|
||||
host);
|
||||
}
|
||||
}
|
||||
|
||||
void add_mysql_user(sqlite3 *handle, const char *user, const char *host,
|
||||
const char *db, bool anydb, const char *pw)
|
||||
void add_mysql_user(sqlite3* handle,
|
||||
const char* user,
|
||||
const char* host,
|
||||
const char* db,
|
||||
bool anydb,
|
||||
const char* pw)
|
||||
{
|
||||
size_t dblen = db && *db ? strlen(db) + 2 : sizeof(null_token); /** +2 for single quotes */
|
||||
size_t dblen = db && *db ? strlen(db) + 2 : sizeof(null_token); /** +2 for single quotes */
|
||||
char dbstr[dblen + 1];
|
||||
|
||||
if (db && *db)
|
||||
@ -431,7 +464,7 @@ void add_mysql_user(sqlite3 *handle, const char *user, const char *host,
|
||||
strcpy(dbstr, null_token);
|
||||
}
|
||||
|
||||
size_t pwlen = pw && *pw ? strlen(pw) + 2 : sizeof(null_token); /** +2 for single quotes */
|
||||
size_t pwlen = pw && *pw ? strlen(pw) + 2 : sizeof(null_token); /** +2 for single quotes */
|
||||
char pwstr[pwlen + 1];
|
||||
|
||||
if (pw && *pw)
|
||||
@ -442,7 +475,9 @@ void add_mysql_user(sqlite3 *handle, const char *user, const char *host,
|
||||
"backend database. MaxScale does not support these "
|
||||
"old passwords. This user will not be able to connect "
|
||||
"via MaxScale. Update the users password to correct "
|
||||
"this.", user, host);
|
||||
"this.",
|
||||
user,
|
||||
host);
|
||||
return;
|
||||
}
|
||||
else if (*pw == '*')
|
||||
@ -461,7 +496,7 @@ void add_mysql_user(sqlite3 *handle, const char *user, const char *host,
|
||||
char insert_sql[len + 1];
|
||||
sprintf(insert_sql, insert_user_query, user, host, dbstr, anydb ? "1" : "0", pwstr);
|
||||
|
||||
char *err;
|
||||
char* err;
|
||||
if (sqlite3_exec(handle, insert_sql, NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to insert user: %s", err);
|
||||
@ -471,14 +506,14 @@ void add_mysql_user(sqlite3 *handle, const char *user, const char *host,
|
||||
MXS_INFO("Added user: %s", insert_sql);
|
||||
}
|
||||
|
||||
static void add_database(sqlite3 *handle, const char *db)
|
||||
static void add_database(sqlite3* handle, const char* db)
|
||||
{
|
||||
size_t len = sizeof(insert_database_query) + strlen(db) + 1;
|
||||
char insert_sql[len + 1];
|
||||
|
||||
sprintf(insert_sql, insert_database_query, db);
|
||||
|
||||
char *err;
|
||||
char* err;
|
||||
if (sqlite3_exec(handle, insert_sql, NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to insert database: %s", err);
|
||||
@ -491,7 +526,7 @@ static void add_database(sqlite3 *handle, const char *db)
|
||||
*
|
||||
* @return An object or NULL if something fails.
|
||||
*/
|
||||
MYSQL *gw_mysql_init()
|
||||
MYSQL* gw_mysql_init()
|
||||
{
|
||||
MYSQL* con = mysql_init(NULL);
|
||||
|
||||
@ -528,22 +563,25 @@ static int gw_mysql_set_timeouts(MYSQL* handle)
|
||||
|
||||
MXS_CONFIG* cnf = config_get_global_options();
|
||||
|
||||
if ((rc = mysql_optionsv(handle, MYSQL_OPT_READ_TIMEOUT,
|
||||
(void *) &cnf->auth_read_timeout)))
|
||||
if ((rc = mysql_optionsv(handle,
|
||||
MYSQL_OPT_READ_TIMEOUT,
|
||||
(void*) &cnf->auth_read_timeout)))
|
||||
{
|
||||
MXS_ERROR("Failed to set read timeout for backend connection.");
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if ((rc = mysql_optionsv(handle, MYSQL_OPT_CONNECT_TIMEOUT,
|
||||
(void *) &cnf->auth_conn_timeout)))
|
||||
if ((rc = mysql_optionsv(handle,
|
||||
MYSQL_OPT_CONNECT_TIMEOUT,
|
||||
(void*) &cnf->auth_conn_timeout)))
|
||||
{
|
||||
MXS_ERROR("Failed to set connect timeout for backend connection.");
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if ((rc = mysql_optionsv(handle, MYSQL_OPT_WRITE_TIMEOUT,
|
||||
(void *) &cnf->auth_write_timeout)))
|
||||
if ((rc = mysql_optionsv(handle,
|
||||
MYSQL_OPT_WRITE_TIMEOUT,
|
||||
(void*) &cnf->auth_write_timeout)))
|
||||
{
|
||||
MXS_ERROR("Failed to set write timeout for backend connection.");
|
||||
goto retblock;
|
||||
@ -562,10 +600,12 @@ retblock:
|
||||
* @return True if the service permissions are OK, false if one or more permissions
|
||||
* are missing.
|
||||
*/
|
||||
static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
const char* user, const char* password)
|
||||
static bool check_server_permissions(SERVICE* service,
|
||||
SERVER* server,
|
||||
const char* user,
|
||||
const char* password)
|
||||
{
|
||||
MYSQL *mysql = gw_mysql_init();
|
||||
MYSQL* mysql = gw_mysql_init();
|
||||
|
||||
if (mysql == NULL)
|
||||
{
|
||||
@ -584,8 +624,12 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
|
||||
MXS_ERROR("[%s] Failed to connect to server '%s' ([%s]:%d) when"
|
||||
" checking authentication user credentials and permissions: %d %s",
|
||||
service->name, server->name, server->address, server->port,
|
||||
my_errno, mysql_error(mysql));
|
||||
service->name,
|
||||
server->name,
|
||||
server->address,
|
||||
server->port,
|
||||
my_errno,
|
||||
mysql_error(mysql));
|
||||
|
||||
mysql_close(mysql);
|
||||
return my_errno != ER_ACCESS_DENIED_ERROR;
|
||||
@ -602,8 +646,8 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
}
|
||||
|
||||
const char* format = "SELECT user, host, %s, Select_priv FROM mysql.user limit 1";
|
||||
const char* query_pw = strstr(server->version_string, "5.7.") ?
|
||||
MYSQL57_PASSWORD : MYSQL_PASSWORD;
|
||||
const char* query_pw = strstr(server->version_string, "5.7.")
|
||||
? MYSQL57_PASSWORD : MYSQL_PASSWORD;
|
||||
char query[strlen(format) + strlen(query_pw) + 1];
|
||||
bool rval = true;
|
||||
sprintf(query, format, query_pw);
|
||||
@ -614,13 +658,17 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
{
|
||||
MXS_ERROR("[%s] User '%s' is missing SELECT privileges"
|
||||
" on mysql.user table. MySQL error message: %s",
|
||||
service->name, user, mysql_error(mysql));
|
||||
service->name,
|
||||
user,
|
||||
mysql_error(mysql));
|
||||
rval = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("[%s] Failed to query from mysql.user table."
|
||||
" MySQL error message: %s", service->name, mysql_error(mysql));
|
||||
" MySQL error message: %s",
|
||||
service->name,
|
||||
mysql_error(mysql));
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -630,7 +678,9 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
if (res == NULL)
|
||||
{
|
||||
MXS_ERROR("[%s] Result retrieval failed when checking for permissions to "
|
||||
"the mysql.user table: %s", service->name, mysql_error(mysql));
|
||||
"the mysql.user table: %s",
|
||||
service->name,
|
||||
mysql_error(mysql));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -644,12 +694,16 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
{
|
||||
MXS_WARNING("[%s] User '%s' is missing SELECT privileges on mysql.db table. "
|
||||
"Database name will be ignored in authentication. "
|
||||
"MySQL error message: %s", service->name, user, mysql_error(mysql));
|
||||
"MySQL error message: %s",
|
||||
service->name,
|
||||
user,
|
||||
mysql_error(mysql));
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("[%s] Failed to query from mysql.db table. MySQL error message: %s",
|
||||
service->name, mysql_error(mysql));
|
||||
service->name,
|
||||
mysql_error(mysql));
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -658,7 +712,9 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
if (res == NULL)
|
||||
{
|
||||
MXS_ERROR("[%s] Result retrieval failed when checking for permissions "
|
||||
"to the mysql.db table: %s", service->name, mysql_error(mysql));
|
||||
"to the mysql.db table: %s",
|
||||
service->name,
|
||||
mysql_error(mysql));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -672,12 +728,17 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
{
|
||||
MXS_WARNING("[%s] User '%s' is missing SELECT privileges on mysql.tables_priv table. "
|
||||
"Database name will be ignored in authentication. "
|
||||
"MySQL error message: %s", service->name, user, mysql_error(mysql));
|
||||
"MySQL error message: %s",
|
||||
service->name,
|
||||
user,
|
||||
mysql_error(mysql));
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("[%s] Failed to query from mysql.tables_priv table. "
|
||||
"MySQL error message: %s", service->name, mysql_error(mysql));
|
||||
"MySQL error message: %s",
|
||||
service->name,
|
||||
mysql_error(mysql));
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -686,7 +747,9 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
if (res == NULL)
|
||||
{
|
||||
MXS_ERROR("[%s] Result retrieval failed when checking for permissions "
|
||||
"to the mysql.tables_priv table: %s", service->name, mysql_error(mysql));
|
||||
"to the mysql.tables_priv table: %s",
|
||||
service->name,
|
||||
mysql_error(mysql));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -695,7 +758,8 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
}
|
||||
|
||||
// Check whether the current user has the SHOW DATABASES privilege
|
||||
if (mxs_mysql_query(mysql, "SELECT show_db_priv FROM mysql.user "
|
||||
if (mxs_mysql_query(mysql,
|
||||
"SELECT show_db_priv FROM mysql.user "
|
||||
"WHERE CONCAT(user, '@', host) = CURRENT_USER()") == 0)
|
||||
{
|
||||
MYSQL_RES* res = mysql_use_result(mysql);
|
||||
@ -707,7 +771,8 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
{
|
||||
MXS_WARNING("[%s] User '%s' is missing the SHOW DATABASES privilege. "
|
||||
"This means that MaxScale cannot see all databases and authentication can fail.",
|
||||
service->name, user);
|
||||
service->name,
|
||||
user);
|
||||
}
|
||||
|
||||
mysql_free_result(res);
|
||||
@ -721,9 +786,9 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
|
||||
|
||||
bool check_service_permissions(SERVICE* service)
|
||||
{
|
||||
if (rcap_type_required(service_get_capabilities(service), RCAP_TYPE_NO_AUTH) ||
|
||||
config_get_global_options()->skip_permission_checks ||
|
||||
service->dbref == NULL) // No servers to check
|
||||
if (rcap_type_required(service_get_capabilities(service), RCAP_TYPE_NO_AUTH)
|
||||
|| config_get_global_options()->skip_permission_checks
|
||||
|| service->dbref == NULL) // No servers to check
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -733,13 +798,13 @@ bool check_service_permissions(SERVICE* service)
|
||||
|
||||
serviceGetUser(service, &user, &password);
|
||||
|
||||
char *dpasswd = decrypt_password(password);
|
||||
char* dpasswd = decrypt_password(password);
|
||||
bool rval = false;
|
||||
|
||||
for (SERVER_REF *server = service->dbref; server; server = server->next)
|
||||
for (SERVER_REF* server = service->dbref; server; server = server->next)
|
||||
{
|
||||
if (server_is_mxs_service(server->server) ||
|
||||
check_server_permissions(service, server->server, user, dpasswd))
|
||||
if (server_is_mxs_service(server->server)
|
||||
|| check_server_permissions(service, server->server, user, dpasswd))
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
@ -760,32 +825,37 @@ bool check_service_permissions(SERVICE* service)
|
||||
*
|
||||
* @return True if the hostname query was successful
|
||||
*/
|
||||
static bool get_hostname(DCB *dcb, char *client_hostname, size_t size)
|
||||
static bool get_hostname(DCB* dcb, char* client_hostname, size_t size)
|
||||
{
|
||||
struct addrinfo *ai = NULL, hint = {};
|
||||
struct addrinfo* ai = NULL, hint = {};
|
||||
hint.ai_flags = AI_ALL;
|
||||
int rc;
|
||||
|
||||
if ((rc = getaddrinfo(dcb->remote, NULL, &hint, &ai)) != 0)
|
||||
{
|
||||
MXS_ERROR("Failed to obtain address for host %s, %s",
|
||||
dcb->remote, gai_strerror(rc));
|
||||
dcb->remote,
|
||||
gai_strerror(rc));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try to lookup the domain name of the given IP-address. This is a slow
|
||||
* i/o-operation, which will stall the entire thread. TODO: cache results
|
||||
* if this feature is used often. */
|
||||
int lookup_result = getnameinfo(ai->ai_addr, ai->ai_addrlen,
|
||||
client_hostname, size,
|
||||
NULL, 0, // No need for the port
|
||||
NI_NAMEREQD); // Text address only
|
||||
int lookup_result = getnameinfo(ai->ai_addr,
|
||||
ai->ai_addrlen,
|
||||
client_hostname,
|
||||
size,
|
||||
NULL,
|
||||
0, // No need for the port
|
||||
NI_NAMEREQD); // Text address only
|
||||
freeaddrinfo(ai);
|
||||
|
||||
if (lookup_result != 0 && lookup_result != EAI_NONAME)
|
||||
{
|
||||
MXS_WARNING("Client hostname lookup failed for '%s', getnameinfo() returned: '%s'.",
|
||||
dcb->remote, gai_strerror(lookup_result));
|
||||
dcb->remote,
|
||||
gai_strerror(lookup_result));
|
||||
}
|
||||
|
||||
return lookup_result == 0;
|
||||
@ -799,8 +869,9 @@ static bool roles_are_available(MYSQL* conn, SERVICE* service, SERVER* server)
|
||||
{
|
||||
static bool log_missing_privs = true;
|
||||
|
||||
if (mxs_mysql_query(conn, "SET @roles_are_available=(SELECT 1 FROM mysql.roles_mapping LIMIT 1)") == 0 &&
|
||||
mxs_mysql_query(conn, "SET @roles_are_available=(SELECT default_role FROM mysql.user LIMIT 1)") == 0)
|
||||
if (mxs_mysql_query(conn, "SET @roles_are_available=(SELECT 1 FROM mysql.roles_mapping LIMIT 1)") == 0
|
||||
&& mxs_mysql_query(conn,
|
||||
"SET @roles_are_available=(SELECT default_role FROM mysql.user LIMIT 1)") == 0)
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
@ -810,24 +881,26 @@ static bool roles_are_available(MYSQL* conn, SERVICE* service, SERVER* server)
|
||||
MXS_WARNING("The user for service '%s' might be missing the SELECT grant on "
|
||||
"`mysql.roles_mapping` or `mysql.user`. Use of default roles is disabled "
|
||||
"until the missing privileges are added. Error was: %s",
|
||||
service->name, mysql_error(conn));
|
||||
service->name,
|
||||
mysql_error(conn));
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
int get_users_from_server(MYSQL *con, SERVER_REF *server_ref, SERVICE *service, SERV_LISTENER *listener)
|
||||
int get_users_from_server(MYSQL* con, SERVER_REF* server_ref, SERVICE* service, SERV_LISTENER* listener)
|
||||
{
|
||||
if (server_ref->server->version_string[0] == 0)
|
||||
{
|
||||
mxs_mysql_set_server_version(con, server_ref->server);
|
||||
}
|
||||
|
||||
char *query = get_users_query(server_ref->server->version_string, service->enable_root,
|
||||
char* query = get_users_query(server_ref->server->version_string,
|
||||
service->enable_root,
|
||||
roles_are_available(con, service, server_ref->server));
|
||||
|
||||
MYSQL_AUTH *instance = (MYSQL_AUTH*)listener->auth_instance;
|
||||
MYSQL_AUTH* instance = (MYSQL_AUTH*)listener->auth_instance;
|
||||
sqlite3* handle = get_handle(instance);
|
||||
int users = 0;
|
||||
|
||||
@ -835,7 +908,7 @@ int get_users_from_server(MYSQL *con, SERVER_REF *server_ref, SERVICE *service,
|
||||
{
|
||||
if (mxs_mysql_query(con, query) == 0)
|
||||
{
|
||||
MYSQL_RES *result = mysql_store_result(con);
|
||||
MYSQL_RES* result = mysql_store_result(con);
|
||||
|
||||
if (result)
|
||||
{
|
||||
@ -853,8 +926,12 @@ int get_users_from_server(MYSQL *con, SERVER_REF *server_ref, SERVICE *service,
|
||||
merge_netmask(row[1]);
|
||||
}
|
||||
|
||||
add_mysql_user(handle, row[0], row[1], row[2],
|
||||
row[3] && strcmp(row[3], "Y") == 0, row[4]);
|
||||
add_mysql_user(handle,
|
||||
row[0],
|
||||
row[1],
|
||||
row[2],
|
||||
row[3] && strcmp(row[3], "Y") == 0,
|
||||
row[4]);
|
||||
users++;
|
||||
}
|
||||
|
||||
@ -872,7 +949,7 @@ int get_users_from_server(MYSQL *con, SERVER_REF *server_ref, SERVICE *service,
|
||||
/** Load the list of databases */
|
||||
if (mxs_mysql_query(con, "SHOW DATABASES") == 0)
|
||||
{
|
||||
MYSQL_RES *result = mysql_store_result(con);
|
||||
MYSQL_RES* result = mysql_store_result(con);
|
||||
if (result)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
@ -900,15 +977,15 @@ int get_users_from_server(MYSQL *con, SERVER_REF *server_ref, SERVICE *service,
|
||||
* @param users The users table into which to load the users
|
||||
* @return -1 on any error or the number of users inserted
|
||||
*/
|
||||
static int get_users(SERV_LISTENER *listener, bool skip_local)
|
||||
static int get_users(SERV_LISTENER* listener, bool skip_local)
|
||||
{
|
||||
const char *service_user = NULL;
|
||||
const char *service_passwd = NULL;
|
||||
SERVICE *service = listener->service;
|
||||
const char* service_user = NULL;
|
||||
const char* service_passwd = NULL;
|
||||
SERVICE* service = listener->service;
|
||||
|
||||
serviceGetUser(service, &service_user, &service_passwd);
|
||||
|
||||
char *dpwd = decrypt_password(service_passwd);
|
||||
char* dpwd = decrypt_password(service_passwd);
|
||||
|
||||
if (dpwd == NULL)
|
||||
{
|
||||
@ -916,33 +993,36 @@ static int get_users(SERV_LISTENER *listener, bool skip_local)
|
||||
}
|
||||
|
||||
/** Delete the old users */
|
||||
MYSQL_AUTH *instance = (MYSQL_AUTH*)listener->auth_instance;
|
||||
MYSQL_AUTH* instance = (MYSQL_AUTH*)listener->auth_instance;
|
||||
sqlite3* handle = get_handle(instance);
|
||||
delete_mysql_users(handle);
|
||||
|
||||
SERVER_REF *server = service->dbref;
|
||||
SERVER_REF* server = service->dbref;
|
||||
int total_users = -1;
|
||||
bool no_active_servers = true;
|
||||
|
||||
for (server = service->dbref; !maxscale_is_shutting_down() && server; server = server->next)
|
||||
{
|
||||
if (!SERVER_REF_IS_ACTIVE(server) || !server_is_active(server->server) ||
|
||||
(skip_local && server_is_mxs_service(server->server)))
|
||||
if (!SERVER_REF_IS_ACTIVE(server) || !server_is_active(server->server)
|
||||
|| (skip_local && server_is_mxs_service(server->server)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
no_active_servers = false;
|
||||
|
||||
MYSQL *con = gw_mysql_init();
|
||||
MYSQL* con = gw_mysql_init();
|
||||
if (con)
|
||||
{
|
||||
if (mxs_mysql_real_connect(con, server->server, service_user, dpwd) == NULL)
|
||||
{
|
||||
MXS_ERROR("Failure loading users data from backend "
|
||||
"[%s:%i] for service [%s]. MySQL error %i, %s",
|
||||
server->server->address, server->server->port,
|
||||
service->name, mysql_errno(con), mysql_error(con));
|
||||
server->server->address,
|
||||
server->server->port,
|
||||
service->name,
|
||||
mysql_errno(con),
|
||||
mysql_error(con));
|
||||
mysql_close(con);
|
||||
}
|
||||
else
|
||||
@ -975,7 +1055,8 @@ static int get_users(SERV_LISTENER *listener, bool skip_local)
|
||||
else if (server == NULL && total_users == -1)
|
||||
{
|
||||
MXS_ERROR("Unable to get user data from backend database for service [%s]."
|
||||
" Failed to connect to any of the backend databases.", service->name);
|
||||
" Failed to connect to any of the backend databases.",
|
||||
service->name);
|
||||
}
|
||||
|
||||
return total_users;
|
||||
|
||||
@ -37,36 +37,38 @@
|
||||
#include <maxscale/utils.h>
|
||||
#include <maxscale/routingworker.h>
|
||||
|
||||
static void* mysql_auth_init(char **options);
|
||||
static bool mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
|
||||
static bool mysql_auth_is_client_ssl_capable(DCB *dcb);
|
||||
static int mysql_auth_authenticate(DCB *dcb);
|
||||
static void mysql_auth_free_client_data(DCB *dcb);
|
||||
static int mysql_auth_load_users(SERV_LISTENER *port);
|
||||
static void *mysql_auth_create(void *instance);
|
||||
static void mysql_auth_destroy(void *data);
|
||||
static void* mysql_auth_init(char** options);
|
||||
static bool mysql_auth_set_protocol_data(DCB* dcb, GWBUF* buf);
|
||||
static bool mysql_auth_is_client_ssl_capable(DCB* dcb);
|
||||
static int mysql_auth_authenticate(DCB* dcb);
|
||||
static void mysql_auth_free_client_data(DCB* dcb);
|
||||
static int mysql_auth_load_users(SERV_LISTENER* port);
|
||||
static void* mysql_auth_create(void* instance);
|
||||
static void mysql_auth_destroy(void* data);
|
||||
|
||||
static int combined_auth_check(
|
||||
DCB *dcb,
|
||||
uint8_t *auth_token,
|
||||
size_t auth_token_len,
|
||||
MySQLProtocol *protocol,
|
||||
char *username,
|
||||
uint8_t *stage1_hash,
|
||||
char *database
|
||||
);
|
||||
static bool mysql_auth_set_client_data(
|
||||
MYSQL_session *client_data,
|
||||
MySQLProtocol *protocol,
|
||||
GWBUF *buffer);
|
||||
static int combined_auth_check(DCB* dcb,
|
||||
uint8_t* auth_token,
|
||||
size_t auth_token_len,
|
||||
MySQLProtocol* protocol,
|
||||
char* username,
|
||||
uint8_t* stage1_hash,
|
||||
char* database
|
||||
);
|
||||
static bool mysql_auth_set_client_data(MYSQL_session* client_data,
|
||||
MySQLProtocol* protocol,
|
||||
GWBUF* buffer);
|
||||
|
||||
void mysql_auth_diagnostic(DCB *dcb, SERV_LISTENER *port);
|
||||
json_t* mysql_auth_diagnostic_json(const SERV_LISTENER *port);
|
||||
void mysql_auth_diagnostic(DCB* dcb, SERV_LISTENER* port);
|
||||
json_t* mysql_auth_diagnostic_json(const SERV_LISTENER* port);
|
||||
|
||||
int mysql_auth_reauthenticate(DCB *dcb, const char *user,
|
||||
uint8_t *token, size_t token_len,
|
||||
uint8_t *scramble, size_t scramble_len,
|
||||
uint8_t *output_token, size_t output_token_len);
|
||||
int mysql_auth_reauthenticate(DCB* dcb,
|
||||
const char* user,
|
||||
uint8_t* token,
|
||||
size_t token_len,
|
||||
uint8_t* scramble,
|
||||
size_t scramble_len,
|
||||
uint8_t* output_token,
|
||||
size_t output_token_len);
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@ -78,45 +80,44 @@ extern "C"
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
mysql_auth_init, /* Initialize the authenticator */
|
||||
NULL, /* Create entry point */
|
||||
mysql_auth_set_protocol_data, /* Extract data into structure */
|
||||
mysql_auth_is_client_ssl_capable, /* Check if client supports SSL */
|
||||
mysql_auth_authenticate, /* Authenticate user credentials */
|
||||
mysql_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* Destroy entry point */
|
||||
mysql_auth_load_users, /* Load users from backend databases */
|
||||
mysql_auth_diagnostic,
|
||||
mysql_auth_diagnostic_json,
|
||||
mysql_auth_reauthenticate /* Handle COM_CHANGE_USER */
|
||||
};
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
mysql_auth_init, /* Initialize the authenticator */
|
||||
NULL, /* Create entry point */
|
||||
mysql_auth_set_protocol_data, /* Extract data into structure */
|
||||
mysql_auth_is_client_ssl_capable, /* Check if client supports SSL */
|
||||
mysql_auth_authenticate, /* Authenticate user credentials */
|
||||
mysql_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* Destroy entry point */
|
||||
mysql_auth_load_users, /* Load users from backend databases */
|
||||
mysql_auth_diagnostic,
|
||||
mysql_auth_diagnostic_json,
|
||||
mysql_auth_reauthenticate /* Handle COM_CHANGE_USER */
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The MySQL client to MaxScale authenticator implementation",
|
||||
"V1.1.0",
|
||||
ACAP_TYPE_ASYNC,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{ { MXS_END_MODULE_PARAMS} }
|
||||
};
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The MySQL client to MaxScale authenticator implementation",
|
||||
"V1.1.0",
|
||||
ACAP_TYPE_ASYNC,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{{MXS_END_MODULE_PARAMS}}
|
||||
};
|
||||
|
||||
return &info;
|
||||
return &info;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool open_instance_database(const char *path, sqlite3 **handle)
|
||||
static bool open_instance_database(const char* path, sqlite3** handle)
|
||||
{
|
||||
int rc = sqlite3_open_v2(path, handle, db_flags, NULL);
|
||||
|
||||
@ -126,11 +127,11 @@ static bool open_instance_database(const char *path, sqlite3 **handle)
|
||||
return false;
|
||||
}
|
||||
|
||||
char *err;
|
||||
char* err;
|
||||
|
||||
if (sqlite3_exec(*handle, users_create_sql, NULL, NULL, &err) != SQLITE_OK ||
|
||||
sqlite3_exec(*handle, databases_create_sql, NULL, NULL, &err) != SQLITE_OK ||
|
||||
sqlite3_exec(*handle, pragma_sql, NULL, NULL, &err) != SQLITE_OK)
|
||||
if (sqlite3_exec(*handle, users_create_sql, NULL, NULL, &err) != SQLITE_OK
|
||||
|| sqlite3_exec(*handle, databases_create_sql, NULL, NULL, &err) != SQLITE_OK
|
||||
|| sqlite3_exec(*handle, pragma_sql, NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to create database: %s", err);
|
||||
sqlite3_free(err);
|
||||
@ -148,7 +149,7 @@ sqlite3* get_handle(MYSQL_AUTH* instance)
|
||||
|
||||
if (instance->handles[i] == NULL)
|
||||
{
|
||||
MXB_AT_DEBUG(bool rval = )open_instance_database(":memory:", &instance->handles[i]);
|
||||
MXB_AT_DEBUG(bool rval = ) open_instance_database(":memory:", &instance->handles[i]);
|
||||
mxb_assert(rval);
|
||||
}
|
||||
|
||||
@ -174,12 +175,12 @@ static bool should_check_permissions(MYSQL_AUTH* instance)
|
||||
* @param options Authenticator options
|
||||
* @return New MYSQL_AUTH instance or NULL on error
|
||||
*/
|
||||
static void* mysql_auth_init(char **options)
|
||||
static void* mysql_auth_init(char** options)
|
||||
{
|
||||
MYSQL_AUTH *instance = static_cast<MYSQL_AUTH*>(MXS_MALLOC(sizeof(*instance)));
|
||||
MYSQL_AUTH* instance = static_cast<MYSQL_AUTH*>(MXS_MALLOC(sizeof(*instance)));
|
||||
|
||||
if (instance &&
|
||||
(instance->handles = static_cast<sqlite3**>(MXS_CALLOC(config_threadcount(), sizeof(sqlite3*)))))
|
||||
if (instance
|
||||
&& (instance->handles = static_cast<sqlite3**>(MXS_CALLOC(config_threadcount(), sizeof(sqlite3*)))))
|
||||
{
|
||||
bool error = false;
|
||||
instance->cache_dir = NULL;
|
||||
@ -190,7 +191,7 @@ static void* mysql_auth_init(char **options)
|
||||
|
||||
for (int i = 0; options[i]; i++)
|
||||
{
|
||||
char *value = strchr(options[i], '=');
|
||||
char* value = strchr(options[i], '=');
|
||||
|
||||
if (value)
|
||||
{
|
||||
@ -198,8 +199,8 @@ static void* mysql_auth_init(char **options)
|
||||
|
||||
if (strcmp(options[i], "cache_dir") == 0)
|
||||
{
|
||||
if ((instance->cache_dir = MXS_STRDUP(value)) == NULL ||
|
||||
!clean_up_pathname(instance->cache_dir))
|
||||
if ((instance->cache_dir = MXS_STRDUP(value)) == NULL
|
||||
|| !clean_up_pathname(instance->cache_dir))
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
@ -246,13 +247,13 @@ static void* mysql_auth_init(char **options)
|
||||
return instance;
|
||||
}
|
||||
|
||||
static bool is_localhost_address(struct sockaddr_storage *addr)
|
||||
static bool is_localhost_address(struct sockaddr_storage* addr)
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
if (addr->ss_family == AF_INET)
|
||||
{
|
||||
struct sockaddr_in *ip = (struct sockaddr_in*)addr;
|
||||
struct sockaddr_in* ip = (struct sockaddr_in*)addr;
|
||||
if (ip->sin_addr.s_addr == INADDR_LOOPBACK)
|
||||
{
|
||||
rval = true;
|
||||
@ -260,7 +261,7 @@ static bool is_localhost_address(struct sockaddr_storage *addr)
|
||||
}
|
||||
else if (addr->ss_family == AF_INET6)
|
||||
{
|
||||
struct sockaddr_in6 *ip = (struct sockaddr_in6*)addr;
|
||||
struct sockaddr_in6* ip = (struct sockaddr_in6*)addr;
|
||||
if (memcmp(&ip->sin6_addr, &in6addr_loopback, sizeof(ip->sin6_addr)) == 0)
|
||||
{
|
||||
rval = true;
|
||||
@ -280,26 +281,32 @@ static bool is_localhost_address(struct sockaddr_storage *addr)
|
||||
* @return Authentication status
|
||||
* @note Authentication status codes are defined in maxscale/protocol/mysql.h
|
||||
*/
|
||||
static int
|
||||
mysql_auth_authenticate(DCB *dcb)
|
||||
static int mysql_auth_authenticate(DCB* dcb)
|
||||
{
|
||||
int auth_ret = ssl_authenticate_check_status(dcb);
|
||||
MYSQL_session *client_data = (MYSQL_session *)dcb->data;
|
||||
MYSQL_session* client_data = (MYSQL_session*)dcb->data;
|
||||
if (auth_ret == MXS_AUTH_SSL_COMPLETE && *client_data->user)
|
||||
{
|
||||
MXS_DEBUG("Receiving connection from '%s' to database '%s'.",
|
||||
client_data->user, client_data->db);
|
||||
client_data->user,
|
||||
client_data->db);
|
||||
|
||||
MYSQL_AUTH *instance = (MYSQL_AUTH*)dcb->listener->auth_instance;
|
||||
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
auth_ret = validate_mysql_user(instance, dcb, client_data,
|
||||
protocol->scramble, sizeof(protocol->scramble));
|
||||
MYSQL_AUTH* instance = (MYSQL_AUTH*)dcb->listener->auth_instance;
|
||||
MySQLProtocol* protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
auth_ret = validate_mysql_user(instance,
|
||||
dcb,
|
||||
client_data,
|
||||
protocol->scramble,
|
||||
sizeof(protocol->scramble));
|
||||
|
||||
if (auth_ret != MXS_AUTH_SUCCEEDED &&
|
||||
service_refresh_users(dcb->service) == 0)
|
||||
if (auth_ret != MXS_AUTH_SUCCEEDED
|
||||
&& service_refresh_users(dcb->service) == 0)
|
||||
{
|
||||
auth_ret = validate_mysql_user(instance, dcb, client_data,
|
||||
protocol->scramble, sizeof(protocol->scramble));
|
||||
auth_ret = validate_mysql_user(instance,
|
||||
dcb,
|
||||
client_data,
|
||||
protocol->scramble,
|
||||
sizeof(protocol->scramble));
|
||||
}
|
||||
|
||||
/* on successful authentication, set user into dcb field */
|
||||
@ -327,21 +334,30 @@ mysql_auth_authenticate(DCB *dcb)
|
||||
{
|
||||
MXS_LOG_EVENT(maxscale::event::AUTHENTICATION_FAILURE,
|
||||
"%s: login attempt for user '%s'@[%s]:%s, authentication failed. %s",
|
||||
dcb->service->name, client_data->user, dcb->remote, dcb->path, extra);
|
||||
dcb->service->name,
|
||||
client_data->user,
|
||||
dcb->remote,
|
||||
dcb->path,
|
||||
extra);
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_LOG_EVENT(maxscale::event::AUTHENTICATION_FAILURE,
|
||||
"%s: login attempt for user '%s'@[%s]:%d, authentication failed. %s",
|
||||
dcb->service->name, client_data->user, dcb->remote, dcb_get_port(dcb), extra);
|
||||
dcb->service->name,
|
||||
client_data->user,
|
||||
dcb->remote,
|
||||
dcb_get_port(dcb),
|
||||
extra);
|
||||
}
|
||||
|
||||
if (is_localhost_address(&dcb->ip) &&
|
||||
!dcb->service->localhost_match_wildcard_host)
|
||||
if (is_localhost_address(&dcb->ip)
|
||||
&& !dcb->service->localhost_match_wildcard_host)
|
||||
{
|
||||
MXS_NOTICE("If you have a wildcard grant that covers this address, "
|
||||
"try adding 'localhost_match_wildcard_host=true' for "
|
||||
"service '%s'. ", dcb->service->name);
|
||||
"service '%s'. ",
|
||||
dcb->service->name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,15 +386,14 @@ mysql_auth_authenticate(DCB *dcb)
|
||||
* @param buffer Pointer to pointer to buffer containing data from client
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
static bool
|
||||
mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
static bool mysql_auth_set_protocol_data(DCB* dcb, GWBUF* buf)
|
||||
{
|
||||
MySQLProtocol *protocol = NULL;
|
||||
MYSQL_session *client_data = NULL;
|
||||
MySQLProtocol* protocol = NULL;
|
||||
MYSQL_session* client_data = NULL;
|
||||
int client_auth_packet_size = 0;
|
||||
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
|
||||
client_data = (MYSQL_session *)dcb->data;
|
||||
client_data = (MYSQL_session*)dcb->data;
|
||||
|
||||
client_auth_packet_size = gwbuf_length(buf);
|
||||
|
||||
@ -418,11 +433,9 @@ mysql_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
* @param client_auth_packet size An integer giving the size of the data
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
static bool
|
||||
mysql_auth_set_client_data(
|
||||
MYSQL_session *client_data,
|
||||
MySQLProtocol *protocol,
|
||||
GWBUF *buffer)
|
||||
static bool mysql_auth_set_client_data(MYSQL_session* client_data,
|
||||
MySQLProtocol* protocol,
|
||||
GWBUF* buffer)
|
||||
{
|
||||
int client_auth_packet_size = gwbuf_length(buffer);
|
||||
uint8_t client_auth_packet[client_auth_packet_size];
|
||||
@ -457,12 +470,14 @@ mysql_auth_set_client_data(
|
||||
{
|
||||
/* Extra 1 is for the terminating null after user name */
|
||||
packet_length_used = MYSQL_AUTH_PACKET_BASE_SIZE + user_length + 1;
|
||||
/* We should find an authentication token next */
|
||||
/* One byte of packet is the length of authentication token */
|
||||
/*
|
||||
* We should find an authentication token next
|
||||
* One byte of packet is the length of authentication token
|
||||
*/
|
||||
client_data->auth_token_len = client_auth_packet[packet_length_used];
|
||||
|
||||
if (client_auth_packet_size >
|
||||
(packet_length_used + client_data->auth_token_len))
|
||||
if (client_auth_packet_size
|
||||
> (packet_length_used + client_data->auth_token_len))
|
||||
{
|
||||
client_data->auth_token = (uint8_t*)MXS_MALLOC(client_data->auth_token_len);
|
||||
|
||||
@ -504,10 +519,9 @@ mysql_auth_set_client_data(
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Boolean indicating whether client is SSL capable
|
||||
*/
|
||||
static bool
|
||||
mysql_auth_is_client_ssl_capable(DCB *dcb)
|
||||
static bool mysql_auth_is_client_ssl_capable(DCB* dcb)
|
||||
{
|
||||
MySQLProtocol *protocol;
|
||||
MySQLProtocol* protocol;
|
||||
|
||||
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
return (protocol->client_capabilities & (int)GW_MYSQL_CAPABILITIES_SSL) ? true : false;
|
||||
@ -525,8 +539,7 @@ mysql_auth_is_client_ssl_capable(DCB *dcb)
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
*/
|
||||
static void
|
||||
mysql_auth_free_client_data(DCB *dcb)
|
||||
static void mysql_auth_free_client_data(DCB* dcb)
|
||||
{
|
||||
MXS_FREE(dcb->data);
|
||||
}
|
||||
@ -537,10 +550,10 @@ mysql_auth_free_client_data(DCB *dcb)
|
||||
* @param port Service listener
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
static bool add_service_user(SERV_LISTENER *port)
|
||||
static bool add_service_user(SERV_LISTENER* port)
|
||||
{
|
||||
const char *user = NULL;
|
||||
const char *password = NULL;
|
||||
const char* user = NULL;
|
||||
const char* password = NULL;
|
||||
bool rval = false;
|
||||
|
||||
serviceGetUser(port->service, &user, &password);
|
||||
@ -549,11 +562,11 @@ static bool add_service_user(SERV_LISTENER *port)
|
||||
|
||||
if ((pw = decrypt_password(password)))
|
||||
{
|
||||
char *newpw = create_hex_sha1_sha1_passwd(pw);
|
||||
char* newpw = create_hex_sha1_sha1_passwd(pw);
|
||||
|
||||
if (newpw)
|
||||
{
|
||||
MYSQL_AUTH *inst = (MYSQL_AUTH*)port->auth_instance;
|
||||
MYSQL_AUTH* inst = (MYSQL_AUTH*)port->auth_instance;
|
||||
sqlite3* handle = get_handle(inst);
|
||||
add_mysql_user(handle, user, "%", "", "Y", newpw);
|
||||
add_mysql_user(handle, user, "localhost", "", "Y", newpw);
|
||||
@ -592,11 +605,11 @@ static bool service_has_servers(SERVICE* service)
|
||||
* @return MXS_AUTH_LOADUSERS_OK on success, MXS_AUTH_LOADUSERS_ERROR and
|
||||
* MXS_AUTH_LOADUSERS_FATAL on fatal error
|
||||
*/
|
||||
static int mysql_auth_load_users(SERV_LISTENER *port)
|
||||
static int mysql_auth_load_users(SERV_LISTENER* port)
|
||||
{
|
||||
int rc = MXS_AUTH_LOADUSERS_OK;
|
||||
SERVICE *service = port->listener->service;
|
||||
MYSQL_AUTH *instance = (MYSQL_AUTH*)port->auth_instance;
|
||||
SERVICE* service = port->listener->service;
|
||||
MYSQL_AUTH* instance = (MYSQL_AUTH*)port->auth_instance;
|
||||
bool first_load = false;
|
||||
|
||||
if (should_check_permissions(instance))
|
||||
@ -618,8 +631,11 @@ static int mysql_auth_load_users(SERV_LISTENER *port)
|
||||
{
|
||||
if (loaded < 0)
|
||||
{
|
||||
MXS_ERROR("[%s] Unable to load users for listener %s listening at [%s]:%d.", service->name,
|
||||
port->name, port->address ? port->address : "::", port->port);
|
||||
MXS_ERROR("[%s] Unable to load users for listener %s listening at [%s]:%d.",
|
||||
service->name,
|
||||
port->name,
|
||||
port->address ? port->address : "::",
|
||||
port->port);
|
||||
}
|
||||
|
||||
if (instance->inject_service_user)
|
||||
@ -643,13 +659,15 @@ static int mysql_auth_load_users(SERV_LISTENER *port)
|
||||
{
|
||||
MXS_NOTICE("[%s] No users were loaded but 'inject_service_user' is enabled. "
|
||||
"Enabling service credentials for authentication until "
|
||||
"database users have been successfully loaded.", service->name);
|
||||
"database users have been successfully loaded.",
|
||||
service->name);
|
||||
}
|
||||
}
|
||||
else if (loaded == 0 && !first_load)
|
||||
{
|
||||
MXS_WARNING("[%s]: failed to load any user information. Authentication"
|
||||
" will probably fail as a result.", service->name);
|
||||
" will probably fail as a result.",
|
||||
service->name);
|
||||
}
|
||||
else if (loaded > 0 && first_load)
|
||||
{
|
||||
@ -659,12 +677,16 @@ 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,
|
||||
uint8_t *output_token, size_t output_token_len)
|
||||
int mysql_auth_reauthenticate(DCB* dcb,
|
||||
const char* user,
|
||||
uint8_t* token,
|
||||
size_t token_len,
|
||||
uint8_t* scramble,
|
||||
size_t scramble_len,
|
||||
uint8_t* output_token,
|
||||
size_t output_token_len)
|
||||
{
|
||||
MYSQL_session *client_data = (MYSQL_session *)dcb->data;
|
||||
MYSQL_session* client_data = (MYSQL_session*)dcb->data;
|
||||
MYSQL_session temp;
|
||||
int rval = 1;
|
||||
|
||||
@ -673,7 +695,7 @@ int mysql_auth_reauthenticate(DCB *dcb, const char *user,
|
||||
temp.auth_token = token;
|
||||
temp.auth_token_len = token_len;
|
||||
|
||||
MYSQL_AUTH *instance = (MYSQL_AUTH*)dcb->listener->auth_instance;
|
||||
MYSQL_AUTH* instance = (MYSQL_AUTH*)dcb->listener->auth_instance;
|
||||
int rc = validate_mysql_user(instance, dcb, &temp, scramble, scramble_len);
|
||||
|
||||
if (rc != MXS_AUTH_SUCCEEDED && service_refresh_users(dcb->service) == 0)
|
||||
@ -688,24 +710,26 @@ int mysql_auth_reauthenticate(DCB *dcb, const char *user,
|
||||
}
|
||||
|
||||
return rval;
|
||||
|
||||
}
|
||||
|
||||
int diag_cb(void *data, int columns, char **row, char **field_names)
|
||||
int diag_cb(void* data, int columns, char** row, char** field_names)
|
||||
{
|
||||
DCB *dcb = (DCB*)data;
|
||||
DCB* dcb = (DCB*)data;
|
||||
dcb_printf(dcb, "%s@%s ", row[0], row[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mysql_auth_diagnostic(DCB *dcb, SERV_LISTENER *port)
|
||||
void mysql_auth_diagnostic(DCB* dcb, SERV_LISTENER* port)
|
||||
{
|
||||
MYSQL_AUTH *instance = (MYSQL_AUTH*)port->auth_instance;
|
||||
MYSQL_AUTH* instance = (MYSQL_AUTH*)port->auth_instance;
|
||||
sqlite3* handle = get_handle(instance);
|
||||
char *err;
|
||||
char* err;
|
||||
|
||||
if (sqlite3_exec(handle, "SELECT user, host FROM " MYSQLAUTH_USERS_TABLE_NAME,
|
||||
diag_cb, dcb, &err) != SQLITE_OK)
|
||||
if (sqlite3_exec(handle,
|
||||
"SELECT user, host FROM " MYSQLAUTH_USERS_TABLE_NAME,
|
||||
diag_cb,
|
||||
dcb,
|
||||
&err) != SQLITE_OK)
|
||||
{
|
||||
dcb_printf(dcb, "Could not access users: %s", err);
|
||||
MXS_ERROR("Could not access users: %s", err);
|
||||
@ -713,7 +737,7 @@ void mysql_auth_diagnostic(DCB *dcb, SERV_LISTENER *port)
|
||||
}
|
||||
}
|
||||
|
||||
int diag_cb_json(void *data, int columns, char **row, char **field_names)
|
||||
int diag_cb_json(void* data, int columns, char** row, char** field_names)
|
||||
{
|
||||
json_t* obj = json_object();
|
||||
json_object_set_new(obj, "user", json_string(row[0]));
|
||||
@ -724,16 +748,19 @@ int diag_cb_json(void *data, int columns, char **row, char **field_names)
|
||||
return 0;
|
||||
}
|
||||
|
||||
json_t* mysql_auth_diagnostic_json(const SERV_LISTENER *port)
|
||||
json_t* mysql_auth_diagnostic_json(const SERV_LISTENER* port)
|
||||
{
|
||||
json_t* rval = json_array();
|
||||
|
||||
MYSQL_AUTH *instance = (MYSQL_AUTH*)port->auth_instance;
|
||||
char *err;
|
||||
MYSQL_AUTH* instance = (MYSQL_AUTH*)port->auth_instance;
|
||||
char* err;
|
||||
sqlite3* handle = get_handle(instance);
|
||||
|
||||
if (sqlite3_exec(handle, "SELECT user, host FROM " MYSQLAUTH_USERS_TABLE_NAME,
|
||||
diag_cb_json, rval, &err) != SQLITE_OK)
|
||||
if (sqlite3_exec(handle,
|
||||
"SELECT user, host FROM " MYSQLAUTH_USERS_TABLE_NAME,
|
||||
diag_cb_json,
|
||||
rval,
|
||||
&err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to print users: %s", err);
|
||||
sqlite3_free(err);
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* @verbatim
|
||||
@ -43,44 +43,44 @@ static const char DBUSERS_DIR[] = "cache";
|
||||
static const char DBUSERS_FILE[] = "dbusers.db";
|
||||
|
||||
/** The table name where we store the users */
|
||||
#define MYSQLAUTH_USERS_TABLE_NAME "mysqlauth_users"
|
||||
#define MYSQLAUTH_USERS_TABLE_NAME "mysqlauth_users"
|
||||
|
||||
/** The table name where we store the users */
|
||||
#define MYSQLAUTH_DATABASES_TABLE_NAME "mysqlauth_databases"
|
||||
#define MYSQLAUTH_DATABASES_TABLE_NAME "mysqlauth_databases"
|
||||
|
||||
/** CREATE TABLE statement for the in-memory users table */
|
||||
static const char users_create_sql[] =
|
||||
"CREATE TABLE IF NOT EXISTS " MYSQLAUTH_USERS_TABLE_NAME
|
||||
"(user varchar(255), host varchar(255), db varchar(255), anydb boolean, password text)";
|
||||
static const char users_create_sql[]
|
||||
= "CREATE TABLE IF NOT EXISTS " MYSQLAUTH_USERS_TABLE_NAME
|
||||
"(user varchar(255), host varchar(255), db varchar(255), anydb boolean, password text)";
|
||||
|
||||
/** CREATE TABLE statement for the in-memory databases table */
|
||||
static const char databases_create_sql[] =
|
||||
"CREATE TABLE IF NOT EXISTS " MYSQLAUTH_DATABASES_TABLE_NAME "(db varchar(255))";
|
||||
static const char databases_create_sql[]
|
||||
= "CREATE TABLE IF NOT EXISTS " MYSQLAUTH_DATABASES_TABLE_NAME "(db varchar(255))";
|
||||
|
||||
/** PRAGMA configuration options for SQLite */
|
||||
static const char pragma_sql[] = "PRAGMA JOURNAL_MODE=NONE";
|
||||
|
||||
/** Query that checks if there's a grant for the user being authenticated */
|
||||
static const char mysqlauth_validate_user_query[] =
|
||||
"SELECT password FROM " MYSQLAUTH_USERS_TABLE_NAME
|
||||
" WHERE user = '%s' AND ( '%s' = host OR '%s' LIKE host) AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db)"
|
||||
" LIMIT 1";
|
||||
static const char mysqlauth_validate_user_query[]
|
||||
= "SELECT password FROM " MYSQLAUTH_USERS_TABLE_NAME
|
||||
" WHERE user = '%s' AND ( '%s' = host OR '%s' LIKE host) AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db)"
|
||||
" LIMIT 1";
|
||||
|
||||
/** Query that checks if there's a grant for the user being authenticated */
|
||||
static const char mysqlauth_validate_user_query_lower[] =
|
||||
"SELECT password FROM " MYSQLAUTH_USERS_TABLE_NAME
|
||||
" WHERE user = '%s' AND ( '%s' = host OR '%s' LIKE host) AND (anydb = '1' OR '%s' = '' OR LOWER('%s') LIKE LOWER(db))"
|
||||
" LIMIT 1";
|
||||
static const char mysqlauth_validate_user_query_lower[]
|
||||
= "SELECT password FROM " MYSQLAUTH_USERS_TABLE_NAME
|
||||
" WHERE user = '%s' AND ( '%s' = host OR '%s' LIKE host) AND (anydb = '1' OR '%s' = '' OR LOWER('%s') LIKE LOWER(db))"
|
||||
" LIMIT 1";
|
||||
|
||||
/** Query that only checks if there's a matching user */
|
||||
static const char mysqlauth_skip_auth_query[] =
|
||||
"SELECT password FROM " MYSQLAUTH_USERS_TABLE_NAME
|
||||
" WHERE user = '%s' AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db)"
|
||||
" LIMIT 1";
|
||||
static const char mysqlauth_skip_auth_query[]
|
||||
= "SELECT password FROM " MYSQLAUTH_USERS_TABLE_NAME
|
||||
" WHERE user = '%s' AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db)"
|
||||
" LIMIT 1";
|
||||
|
||||
/** Query that checks that the database exists */
|
||||
static const char mysqlauth_validate_database_query[] =
|
||||
"SELECT * FROM " MYSQLAUTH_DATABASES_TABLE_NAME " WHERE db = '%s' LIMIT 1";
|
||||
static const char mysqlauth_validate_database_query[]
|
||||
= "SELECT * FROM " MYSQLAUTH_DATABASES_TABLE_NAME " WHERE db = '%s' LIMIT 1";
|
||||
|
||||
/** Delete query used to clean up the database before loading new users */
|
||||
static const char delete_users_query[] = "DELETE FROM " MYSQLAUTH_USERS_TABLE_NAME;
|
||||
@ -89,35 +89,35 @@ static const char delete_users_query[] = "DELETE FROM " MYSQLAUTH_USERS_TABLE_NA
|
||||
static const char delete_databases_query[] = "DELETE FROM " MYSQLAUTH_DATABASES_TABLE_NAME;
|
||||
|
||||
/** The insert query template which adds users to the mysqlauth_users table */
|
||||
static const char insert_user_query[] =
|
||||
"INSERT OR REPLACE INTO " MYSQLAUTH_USERS_TABLE_NAME " VALUES ('%s', '%s', %s, %s, %s)";
|
||||
static const char insert_user_query[]
|
||||
= "INSERT OR REPLACE INTO " MYSQLAUTH_USERS_TABLE_NAME " VALUES ('%s', '%s', %s, %s, %s)";
|
||||
|
||||
/** The insert query template which adds the databases to the table */
|
||||
static const char insert_database_query[] =
|
||||
"INSERT OR REPLACE INTO " MYSQLAUTH_DATABASES_TABLE_NAME " VALUES ('%s')";
|
||||
static const char insert_database_query[]
|
||||
= "INSERT OR REPLACE INTO " MYSQLAUTH_DATABASES_TABLE_NAME " VALUES ('%s')";
|
||||
|
||||
static const char dump_users_query[] =
|
||||
"SELECT user, host, db, anydb, password FROM " MYSQLAUTH_USERS_TABLE_NAME;
|
||||
static const char dump_users_query[]
|
||||
= "SELECT user, host, db, anydb, password FROM " MYSQLAUTH_USERS_TABLE_NAME;
|
||||
|
||||
static const char dump_databases_query[] =
|
||||
"SELECT db FROM " MYSQLAUTH_DATABASES_TABLE_NAME;
|
||||
static const char dump_databases_query[]
|
||||
= "SELECT db FROM " MYSQLAUTH_DATABASES_TABLE_NAME;
|
||||
|
||||
/** 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_NOMUTEX;
|
||||
static int db_flags = SQLITE_OPEN_READWRITE
|
||||
| SQLITE_OPEN_CREATE
|
||||
| SQLITE_OPEN_NOMUTEX;
|
||||
|
||||
typedef struct mysql_auth
|
||||
{
|
||||
sqlite3 **handles; /**< 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 */
|
||||
bool check_permissions;
|
||||
bool lower_case_table_names; /**< Disable database case-sensitivity */
|
||||
sqlite3** handles; /**< 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 */
|
||||
bool check_permissions;
|
||||
bool lower_case_table_names; /**< Disable database case-sensitivity */
|
||||
} MYSQL_AUTH;
|
||||
|
||||
/**
|
||||
@ -125,11 +125,11 @@ typedef struct mysql_auth
|
||||
*/
|
||||
typedef struct mysql_user_host_key
|
||||
{
|
||||
char *user;
|
||||
char* user;
|
||||
struct sockaddr_in ipv4;
|
||||
int netmask;
|
||||
char *resource;
|
||||
char hostname[MYSQL_HOST_MAXLEN + 1];
|
||||
int netmask;
|
||||
char* resource;
|
||||
char hostname[MYSQL_HOST_MAXLEN + 1];
|
||||
} MYSQL_USER_HOST;
|
||||
|
||||
/**
|
||||
@ -150,8 +150,12 @@ sqlite3* get_handle(MYSQL_AUTH* instance);
|
||||
* @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);
|
||||
void add_mysql_user(sqlite3* handle,
|
||||
const char* user,
|
||||
const char* host,
|
||||
const char* db,
|
||||
bool anydb,
|
||||
const char* pw);
|
||||
|
||||
/**
|
||||
* @brief Check if the service user has all required permissions to operate properly.
|
||||
@ -174,7 +178,7 @@ bool check_service_permissions(SERVICE* service);
|
||||
*
|
||||
* @return True on success
|
||||
*/
|
||||
bool dbusers_load(sqlite3 *handle, const char *filename);
|
||||
bool dbusers_load(sqlite3* handle, const char* filename);
|
||||
|
||||
/**
|
||||
* Save users to persisted database
|
||||
@ -183,7 +187,7 @@ bool dbusers_load(sqlite3 *handle, const char *filename);
|
||||
*
|
||||
* @return True on success
|
||||
*/
|
||||
bool dbusers_save(sqlite3 *src, const char *filename);
|
||||
bool dbusers_save(sqlite3* src, const char* filename);
|
||||
|
||||
/**
|
||||
* Reload and replace the currently loaded database users
|
||||
@ -193,7 +197,7 @@ bool dbusers_save(sqlite3 *src, const char *filename);
|
||||
*
|
||||
* @return -1 on any error or the number of users inserted (0 means no users at all)
|
||||
*/
|
||||
int replace_mysql_users(SERV_LISTENER *listener, bool skip_local);
|
||||
int replace_mysql_users(SERV_LISTENER* listener, bool skip_local);
|
||||
|
||||
/**
|
||||
* @brief Verify the user has access to the database
|
||||
@ -206,7 +210,10 @@ int replace_mysql_users(SERV_LISTENER *listener, bool skip_local);
|
||||
*
|
||||
* @return MXS_AUTH_SUCCEEDED if the user has access to the database
|
||||
*/
|
||||
int validate_mysql_user(MYSQL_AUTH* instance, DCB *dcb, MYSQL_session *session,
|
||||
uint8_t *scramble, size_t scramble_len);
|
||||
int validate_mysql_user(MYSQL_AUTH* instance,
|
||||
DCB* dcb,
|
||||
MYSQL_session* session,
|
||||
uint8_t* scramble,
|
||||
size_t scramble_len);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
||||
@ -41,14 +41,14 @@ enum mba_state
|
||||
/** Structure representing the authentication state */
|
||||
typedef struct mysql_backend_auth
|
||||
{
|
||||
enum mba_state state; /**< Authentication state */
|
||||
enum mba_state state; /**< Authentication state */
|
||||
} mysql_backend_auth_t;
|
||||
|
||||
/**
|
||||
* @brief Allocate a new mysql_backend_auth object
|
||||
* @return Allocated object or NULL if memory allocation failed
|
||||
*/
|
||||
void* auth_backend_create(void *instance)
|
||||
void* auth_backend_create(void* instance)
|
||||
{
|
||||
mysql_backend_auth_t* mba = static_cast<mysql_backend_auth_t*>(MXS_MALLOC(sizeof(*mba)));
|
||||
|
||||
@ -64,7 +64,7 @@ void* auth_backend_create(void *instance)
|
||||
* @brief Free allocated mysql_backend_auth object
|
||||
* @param data Allocated mysql_backend_auth object
|
||||
*/
|
||||
void auth_backend_destroy(void *data)
|
||||
void auth_backend_destroy(void* data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
@ -79,10 +79,10 @@ void auth_backend_destroy(void *data)
|
||||
* @return True on success, false on error
|
||||
* @see authenticator.h
|
||||
*/
|
||||
static bool auth_backend_extract(DCB *dcb, GWBUF *buf)
|
||||
static bool auth_backend_extract(DCB* dcb, GWBUF* buf)
|
||||
{
|
||||
bool rval = false;
|
||||
mysql_backend_auth_t *mba = (mysql_backend_auth_t*)dcb->authenticator_data;
|
||||
mysql_backend_auth_t* mba = (mysql_backend_auth_t*)dcb->authenticator_data;
|
||||
|
||||
switch (mba->state)
|
||||
{
|
||||
@ -114,10 +114,10 @@ static bool auth_backend_extract(DCB *dcb, GWBUF *buf)
|
||||
* @return Authentication status
|
||||
* @see authenticator.h
|
||||
*/
|
||||
static int auth_backend_authenticate(DCB *dcb)
|
||||
static int auth_backend_authenticate(DCB* dcb)
|
||||
{
|
||||
int rval = MXS_AUTH_FAILED;
|
||||
mysql_backend_auth_t *mba = (mysql_backend_auth_t*)dcb->authenticator_data;
|
||||
mysql_backend_auth_t* mba = (mysql_backend_auth_t*)dcb->authenticator_data;
|
||||
|
||||
if (mba->state == MBA_AUTH_OK)
|
||||
{
|
||||
@ -138,7 +138,7 @@ static int auth_backend_authenticate(DCB *dcb)
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Boolean indicating whether client is SSL capable
|
||||
*/
|
||||
static bool auth_backend_ssl(DCB *dcb)
|
||||
static bool auth_backend_ssl(DCB* dcb)
|
||||
{
|
||||
return dcb->server->server_ssl != NULL;
|
||||
}
|
||||
@ -153,42 +153,42 @@ extern "C"
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
auth_backend_create, /* Create authenticator */
|
||||
auth_backend_extract, /* Extract data into structure */
|
||||
auth_backend_ssl, /* Check if client supports SSL */
|
||||
auth_backend_authenticate, /* Authenticate user credentials */
|
||||
NULL, /* The shared data is freed by the client DCB */
|
||||
auth_backend_destroy, /* Destroy authenticator */
|
||||
NULL, /* We don't need to load users */
|
||||
NULL, /* No diagnostic */
|
||||
NULL,
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The MySQL MaxScale to backend server authenticator",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
{MXS_END_MODULE_PARAMS}
|
||||
}
|
||||
};
|
||||
NULL, /* No initialize entry point */
|
||||
auth_backend_create, /* Create authenticator */
|
||||
auth_backend_extract, /* Extract data into structure */
|
||||
auth_backend_ssl, /* Check if client supports SSL */
|
||||
auth_backend_authenticate, /* Authenticate user credentials */
|
||||
NULL, /* The shared data is freed by the client DCB */
|
||||
auth_backend_destroy, /* Destroy authenticator */
|
||||
NULL, /* We don't need to load users */
|
||||
NULL, /* No diagnostic */
|
||||
NULL,
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The MySQL MaxScale to backend server authenticator",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{
|
||||
{MXS_END_MODULE_PARAMS}
|
||||
}
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
/*lint +e14 */
|
||||
}
|
||||
|
||||
@ -38,10 +38,10 @@
|
||||
/** MXS-1026: Without MySQL protocol data structures, the NullAuth authenticator will crash. */
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
|
||||
static bool null_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
|
||||
static bool null_auth_is_client_ssl_capable(DCB *dcb);
|
||||
static int null_auth_authenticate(DCB *dcb);
|
||||
static void null_auth_free_client_data(DCB *dcb);
|
||||
static bool null_auth_set_protocol_data(DCB* dcb, GWBUF* buf);
|
||||
static bool null_auth_is_client_ssl_capable(DCB* dcb);
|
||||
static int null_auth_authenticate(DCB* dcb);
|
||||
static void null_auth_free_client_data(DCB* dcb);
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@ -53,43 +53,43 @@ extern "C"
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
NULL, /* No create entry point */
|
||||
null_auth_set_protocol_data, /* Extract data into structure */
|
||||
null_auth_is_client_ssl_capable, /* Check if client supports SSL */
|
||||
null_auth_authenticate, /* Authenticate user credentials */
|
||||
null_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers, /* Load generic users */
|
||||
NULL, /* No diagnostic */
|
||||
NULL,
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The Null client authenticator implementation",
|
||||
"V1.1.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
{MXS_END_MODULE_PARAMS}
|
||||
}
|
||||
};
|
||||
NULL, /* No initialize entry point */
|
||||
NULL, /* No create entry point */
|
||||
null_auth_set_protocol_data, /* Extract data into structure */
|
||||
null_auth_is_client_ssl_capable,/* Check if client supports SSL */
|
||||
null_auth_authenticate, /* Authenticate user credentials */
|
||||
null_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers, /* Load generic users */
|
||||
NULL, /* No diagnostic */
|
||||
NULL,
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The Null client authenticator implementation",
|
||||
"V1.1.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{
|
||||
{MXS_END_MODULE_PARAMS}
|
||||
}
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
/*lint +e14 */
|
||||
}
|
||||
|
||||
@ -101,8 +101,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Authentication status - always 0 to denote success
|
||||
*/
|
||||
static int
|
||||
null_auth_authenticate(DCB *dcb)
|
||||
static int null_auth_authenticate(DCB* dcb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -116,8 +115,7 @@ null_auth_authenticate(DCB *dcb)
|
||||
* @param buffer Pointer to pointer to buffer containing data from client
|
||||
* @return Always true
|
||||
*/
|
||||
static bool
|
||||
null_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
static bool null_auth_set_protocol_data(DCB* dcb, GWBUF* buf)
|
||||
{
|
||||
/** MXS-1026: This will just prevent a crash when the NullAuth authenticator
|
||||
* is used. This does not provide a way to use MaxScale with no authentication. */
|
||||
@ -135,8 +133,7 @@ null_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Boolean indicating whether client is SSL capable - always true
|
||||
*/
|
||||
static bool
|
||||
null_auth_is_client_ssl_capable(DCB *dcb)
|
||||
static bool null_auth_is_client_ssl_capable(DCB* dcb)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -148,8 +145,7 @@ null_auth_is_client_ssl_capable(DCB *dcb)
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
*/
|
||||
static void
|
||||
null_auth_free_client_data(DCB *dcb)
|
||||
static void null_auth_free_client_data(DCB* dcb)
|
||||
{
|
||||
free(dcb->data);
|
||||
dcb->data = NULL;
|
||||
|
||||
@ -35,10 +35,10 @@
|
||||
#include <maxscale/buffer.h>
|
||||
#include <maxscale/users.h>
|
||||
|
||||
static bool null_auth_set_protocol_data(DCB *dcb, GWBUF *buf);
|
||||
static bool null_auth_is_client_ssl_capable(DCB *dcb);
|
||||
static int null_auth_authenticate(DCB *dcb);
|
||||
static void null_auth_free_client_data(DCB *dcb);
|
||||
static bool null_auth_set_protocol_data(DCB* dcb, GWBUF* buf);
|
||||
static bool null_auth_is_client_ssl_capable(DCB* dcb);
|
||||
static int null_auth_authenticate(DCB* dcb);
|
||||
static void null_auth_free_client_data(DCB* dcb);
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@ -50,43 +50,43 @@ extern "C"
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
NULL, /* No create entry point */
|
||||
null_auth_set_protocol_data, /* Extract data into structure */
|
||||
null_auth_is_client_ssl_capable, /* Check if client supports SSL */
|
||||
null_auth_authenticate, /* Authenticate user credentials */
|
||||
null_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers, /* Load generic users */
|
||||
NULL, /* No diagnostic */
|
||||
NULL,
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The Null client authenticator implementation",
|
||||
"V1.1.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
{MXS_END_MODULE_PARAMS}
|
||||
}
|
||||
};
|
||||
NULL, /* No initialize entry point */
|
||||
NULL, /* No create entry point */
|
||||
null_auth_set_protocol_data, /* Extract data into structure */
|
||||
null_auth_is_client_ssl_capable,/* Check if client supports SSL */
|
||||
null_auth_authenticate, /* Authenticate user credentials */
|
||||
null_auth_free_client_data, /* Free the client data held in DCB */
|
||||
NULL, /* No destroy entry point */
|
||||
users_default_loadusers, /* Load generic users */
|
||||
NULL, /* No diagnostic */
|
||||
NULL,
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"The Null client authenticator implementation",
|
||||
"V1.1.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{
|
||||
{MXS_END_MODULE_PARAMS}
|
||||
}
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
/*lint +e14 */
|
||||
}
|
||||
|
||||
@ -98,8 +98,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Authentication status - always 1 to denote failure
|
||||
*/
|
||||
static int
|
||||
null_auth_authenticate(DCB *dcb)
|
||||
static int null_auth_authenticate(DCB* dcb)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@ -113,8 +112,7 @@ null_auth_authenticate(DCB *dcb)
|
||||
* @param buffer Pointer to pointer to buffer containing data from client
|
||||
* @return Always true
|
||||
*/
|
||||
static bool
|
||||
null_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
static bool null_auth_set_protocol_data(DCB* dcb, GWBUF* buf)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -128,8 +126,7 @@ null_auth_set_protocol_data(DCB *dcb, GWBUF *buf)
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Boolean indicating whether client is SSL capable - always true
|
||||
*/
|
||||
static bool
|
||||
null_auth_is_client_ssl_capable(DCB *dcb)
|
||||
static bool null_auth_is_client_ssl_capable(DCB* dcb)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -141,5 +138,6 @@ null_auth_is_client_ssl_capable(DCB *dcb)
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
*/
|
||||
static void
|
||||
null_auth_free_client_data(DCB *dcb) {}
|
||||
static void null_auth_free_client_data(DCB* dcb)
|
||||
{
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
#include"pam_auth.hh"
|
||||
#include "pam_auth.hh"
|
||||
|
||||
#include <string>
|
||||
#include <maxscale/authenticator.h>
|
||||
@ -35,7 +35,7 @@ const int NUM_FIELDS = 5;
|
||||
*
|
||||
* @return Authenticator instance, or NULL on error
|
||||
*/
|
||||
static void* pam_auth_init(char **options)
|
||||
static void* pam_auth_init(char** options)
|
||||
{
|
||||
return PamInstance::create(options);
|
||||
}
|
||||
@ -47,7 +47,7 @@ static void* pam_auth_init(char **options)
|
||||
*
|
||||
* @return Authenticator session
|
||||
*/
|
||||
static void* pam_auth_alloc(void *instance)
|
||||
static void* pam_auth_alloc(void* instance)
|
||||
{
|
||||
PamInstance* inst = static_cast<PamInstance*>(instance);
|
||||
return PamClientSession::create(*inst);
|
||||
@ -58,7 +58,7 @@ static void* pam_auth_alloc(void *instance)
|
||||
*
|
||||
* @param data PAM session
|
||||
*/
|
||||
static void pam_auth_free(void *data)
|
||||
static void pam_auth_free(void* data)
|
||||
{
|
||||
delete static_cast<PamClientSession*>(data);
|
||||
}
|
||||
@ -72,9 +72,9 @@ static void pam_auth_free(void *data)
|
||||
* @return True if authentication can continue, false if
|
||||
* authentication failed
|
||||
*/
|
||||
static bool pam_auth_extract(DCB *dcb, GWBUF *read_buffer)
|
||||
static bool pam_auth_extract(DCB* dcb, GWBUF* read_buffer)
|
||||
{
|
||||
PamClientSession *pses = static_cast<PamClientSession*>(dcb->authenticator_data);
|
||||
PamClientSession* pses = static_cast<PamClientSession*>(dcb->authenticator_data);
|
||||
return pses->extract(dcb, read_buffer);
|
||||
}
|
||||
|
||||
@ -85,9 +85,9 @@ static bool pam_auth_extract(DCB *dcb, GWBUF *read_buffer)
|
||||
*
|
||||
* @return True if client supports SSL
|
||||
*/
|
||||
static bool pam_auth_connectssl(DCB *dcb)
|
||||
static bool pam_auth_connectssl(DCB* dcb)
|
||||
{
|
||||
MySQLProtocol *protocol = (MySQLProtocol*)dcb->protocol;
|
||||
MySQLProtocol* protocol = (MySQLProtocol*)dcb->protocol;
|
||||
return protocol->client_capabilities & GW_MYSQL_CAPABILITIES_SSL;
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ static bool pam_auth_connectssl(DCB *dcb)
|
||||
* if authentication was successfully completed. MXS_AUTH_FAILED if authentication
|
||||
* has failed.
|
||||
*/
|
||||
static int pam_auth_authenticate(DCB *dcb)
|
||||
static int pam_auth_authenticate(DCB* dcb)
|
||||
{
|
||||
PamClientSession* pses = static_cast<PamClientSession*>(dcb->authenticator_data);
|
||||
return pses->authenticate(dcb);
|
||||
@ -113,11 +113,11 @@ static int pam_auth_authenticate(DCB *dcb)
|
||||
*
|
||||
* @param dcb DCB to free data from
|
||||
*/
|
||||
static void pam_auth_free_data(DCB *dcb)
|
||||
static void pam_auth_free_data(DCB* dcb)
|
||||
{
|
||||
if (dcb->data)
|
||||
{
|
||||
MYSQL_session *ses = (MYSQL_session *)dcb->data;
|
||||
MYSQL_session* ses = (MYSQL_session*)dcb->data;
|
||||
MXS_FREE(ses->auth_token);
|
||||
MXS_FREE(ses);
|
||||
dcb->data = NULL;
|
||||
@ -134,21 +134,21 @@ static void pam_auth_free_data(DCB *dcb)
|
||||
*
|
||||
* @return MXS_AUTH_LOADUSERS_OK on success, MXS_AUTH_LOADUSERS_ERROR on error
|
||||
*/
|
||||
static int pam_auth_load_users(SERV_LISTENER *listener)
|
||||
static int pam_auth_load_users(SERV_LISTENER* listener)
|
||||
{
|
||||
PamInstance *inst = static_cast<PamInstance*>(listener->auth_instance);
|
||||
PamInstance* inst = static_cast<PamInstance*>(listener->auth_instance);
|
||||
return inst->load_users(listener->service);
|
||||
}
|
||||
|
||||
static void pam_auth_diagnostic(DCB *dcb, SERV_LISTENER *listener)
|
||||
static void pam_auth_diagnostic(DCB* dcb, SERV_LISTENER* listener)
|
||||
{
|
||||
PamInstance *inst = static_cast<PamInstance*>(listener->auth_instance);
|
||||
PamInstance* inst = static_cast<PamInstance*>(listener->auth_instance);
|
||||
inst->diagnostic(dcb);
|
||||
}
|
||||
|
||||
static json_t* pam_auth_diagnostic_json(const SERV_LISTENER *listener)
|
||||
static json_t* pam_auth_diagnostic_json(const SERV_LISTENER* listener)
|
||||
{
|
||||
PamInstance *inst = static_cast<PamInstance*>(listener->auth_instance);
|
||||
PamInstance* inst = static_cast<PamInstance*>(listener->auth_instance);
|
||||
return inst->diagnostic_json();
|
||||
}
|
||||
|
||||
@ -157,40 +157,39 @@ extern "C"
|
||||
/**
|
||||
* Module handle entry point
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
pam_auth_init, /* Initialize authenticator */
|
||||
pam_auth_alloc, /* Allocate authenticator data */
|
||||
pam_auth_extract, /* Extract data into structure */
|
||||
pam_auth_connectssl, /* Check if client supports SSL */
|
||||
pam_auth_authenticate, /* Authenticate user credentials */
|
||||
pam_auth_free_data, /* Free the client data held in DCB */
|
||||
pam_auth_free, /* Free authenticator data */
|
||||
pam_auth_load_users, /* Load database users */
|
||||
pam_auth_diagnostic, /* Default user diagnostic */
|
||||
pam_auth_diagnostic_json, /* Default user diagnostic */
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
pam_auth_init, /* Initialize authenticator */
|
||||
pam_auth_alloc, /* Allocate authenticator data */
|
||||
pam_auth_extract, /* Extract data into structure */
|
||||
pam_auth_connectssl, /* Check if client supports SSL */
|
||||
pam_auth_authenticate, /* Authenticate user credentials */
|
||||
pam_auth_free_data, /* Free the client data held in DCB */
|
||||
pam_auth_free, /* Free authenticator data */
|
||||
pam_auth_load_users, /* Load database users */
|
||||
pam_auth_diagnostic, /* Default user diagnostic */
|
||||
pam_auth_diagnostic_json, /* Default user diagnostic */
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"PAM authenticator",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{ { MXS_END_MODULE_PARAMS} }
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_GA,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"PAM authenticator",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{{MXS_END_MODULE_PARAMS}}
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Common definitions and includes for PAM client authenticator
|
||||
@ -32,4 +32,4 @@ extern const string FIELD_HOST;
|
||||
extern const string FIELD_DB;
|
||||
extern const string FIELD_ANYDB;
|
||||
extern const string FIELD_AUTHSTR;
|
||||
extern const int NUM_FIELDS;
|
||||
extern const int NUM_FIELDS;
|
||||
|
||||
@ -30,7 +30,7 @@ namespace
|
||||
*
|
||||
* @return True on success, false if memory allocation failed
|
||||
*/
|
||||
bool store_client_password(DCB *dcb, GWBUF *buffer)
|
||||
bool store_client_password(DCB* dcb, GWBUF* buffer)
|
||||
{
|
||||
bool rval = false;
|
||||
uint8_t header[MYSQL_HEADER_LEN];
|
||||
@ -38,8 +38,8 @@ bool store_client_password(DCB *dcb, GWBUF *buffer)
|
||||
if (gwbuf_copy_data(buffer, 0, MYSQL_HEADER_LEN, header) == MYSQL_HEADER_LEN)
|
||||
{
|
||||
size_t plen = gw_mysql_get_byte3(header);
|
||||
MYSQL_session *ses = (MYSQL_session*)dcb->data;
|
||||
ses->auth_token = (uint8_t *)MXS_CALLOC(plen, sizeof(uint8_t));
|
||||
MYSQL_session* ses = (MYSQL_session*)dcb->data;
|
||||
ses->auth_token = (uint8_t*)MXS_CALLOC(plen, sizeof(uint8_t));
|
||||
if (ses->auth_token)
|
||||
{
|
||||
ses->auth_token_len = gwbuf_copy_data(buffer, MYSQL_HEADER_LEN, plen, ses->auth_token);
|
||||
@ -59,7 +59,7 @@ bool store_client_password(DCB *dcb, GWBUF *buffer)
|
||||
* @param column_names Column names
|
||||
* @return Always 0
|
||||
*/
|
||||
int user_services_cb(void *data, int columns, char** column_vals, char** column_names)
|
||||
int user_services_cb(void* data, int columns, char** column_vals, char** column_names)
|
||||
{
|
||||
mxb_assert(columns == 1);
|
||||
PamClientSession::StringVector* results = static_cast<PamClientSession::StringVector*>(data);
|
||||
@ -78,14 +78,14 @@ int user_services_cb(void *data, int columns, char** column_vals, char** column_
|
||||
/** Used by the PAM conversation function */
|
||||
struct ConversationData
|
||||
{
|
||||
DCB* m_client;
|
||||
int m_counter;
|
||||
DCB* m_client;
|
||||
int m_counter;
|
||||
string m_password;
|
||||
|
||||
ConversationData(DCB* client, int counter, const string& password)
|
||||
: m_client(client),
|
||||
m_counter(counter),
|
||||
m_password(password)
|
||||
: m_client(client)
|
||||
, m_counter(counter)
|
||||
, m_password(password)
|
||||
{
|
||||
}
|
||||
};
|
||||
@ -97,8 +97,10 @@ struct ConversationData
|
||||
* http://www.linux-pam.org/Linux-PAM-html/adg-interface-of-app-expected.html#adg-pam_conv
|
||||
* for more information.
|
||||
*/
|
||||
int conversation_func(int num_msg, const struct pam_message **msg,
|
||||
struct pam_response **resp_out, void *appdata_ptr)
|
||||
int conversation_func(int num_msg,
|
||||
const struct pam_message** msg,
|
||||
struct pam_response** resp_out,
|
||||
void* appdata_ptr)
|
||||
{
|
||||
MXS_DEBUG("Entering PAM conversation function.");
|
||||
int rval = PAM_CONV_ERR;
|
||||
@ -106,16 +108,18 @@ int conversation_func(int num_msg, const struct pam_message **msg,
|
||||
if (data->m_counter > 1)
|
||||
{
|
||||
MXS_ERROR("Multiple calls to conversation function for client '%s'. %s",
|
||||
data->m_client->user, GENERAL_ERRMSG);
|
||||
data->m_client->user,
|
||||
GENERAL_ERRMSG);
|
||||
}
|
||||
else if (num_msg == 1)
|
||||
{
|
||||
pam_message first = *msg[0];
|
||||
if ((first.msg_style != PAM_PROMPT_ECHO_OFF && first.msg_style != PAM_PROMPT_ECHO_ON) ||
|
||||
PASSWORD != first.msg)
|
||||
if ((first.msg_style != PAM_PROMPT_ECHO_OFF && first.msg_style != PAM_PROMPT_ECHO_ON)
|
||||
|| PASSWORD != first.msg)
|
||||
{
|
||||
MXS_ERROR("Unexpected PAM message: type='%d', contents='%s'",
|
||||
first.msg_style, first.msg);
|
||||
first.msg_style,
|
||||
first.msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -132,7 +136,8 @@ int conversation_func(int num_msg, const struct pam_message **msg,
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Conversation function received '%d' messages from API. Only "
|
||||
"singular messages are supported.", num_msg);
|
||||
"singular messages are supported.",
|
||||
num_msg);
|
||||
}
|
||||
data->m_counter++;
|
||||
return rval;
|
||||
@ -167,16 +172,22 @@ bool validate_pam_password(const string& user, const string& password, const str
|
||||
authenticated = true;
|
||||
MXS_DEBUG("pam_authenticate returned success.");
|
||||
break;
|
||||
|
||||
case PAM_USER_UNKNOWN:
|
||||
case PAM_AUTH_ERR:
|
||||
// Normal failure, username or password was wrong.
|
||||
MXS_LOG_EVENT(maxscale::event::AUTHENTICATION_FAILURE,
|
||||
PAM_AUTH_ERR_MSG, user.c_str(), pam_strerror(pam_handle, pam_status));
|
||||
PAM_AUTH_ERR_MSG,
|
||||
user.c_str(),
|
||||
pam_strerror(pam_handle, pam_status));
|
||||
break;
|
||||
|
||||
default:
|
||||
// More exotic error
|
||||
MXS_LOG_EVENT(maxscale::event::AUTHENTICATION_FAILURE,
|
||||
PAM_AUTH_ERR_MSG, user.c_str(), pam_strerror(pam_handle, pam_status));
|
||||
PAM_AUTH_ERR_MSG,
|
||||
user.c_str(),
|
||||
pam_strerror(pam_handle, pam_status));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -193,6 +204,7 @@ bool validate_pam_password(const string& user, const string& password, const str
|
||||
case PAM_SUCCESS:
|
||||
account_ok = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Credentials have already been checked to be ok, so this is again a bit of an exotic error.
|
||||
MXS_ERROR(PAM_ACC_ERR_MSG, user.c_str(), pam_strerror(pam_handle, pam_status));
|
||||
@ -202,18 +214,17 @@ bool validate_pam_password(const string& user, const string& password, const str
|
||||
pam_end(pam_handle, pam_status);
|
||||
return account_ok;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PamClientSession::PamClientSession(sqlite3* dbhandle, const PamInstance& instance)
|
||||
: m_state(PAM_AUTH_INIT),
|
||||
m_sequence(0),
|
||||
m_dbhandle(dbhandle),
|
||||
m_instance(instance)
|
||||
: m_state(PAM_AUTH_INIT)
|
||||
, m_sequence(0)
|
||||
, m_dbhandle(dbhandle)
|
||||
, m_instance(instance)
|
||||
{
|
||||
}
|
||||
|
||||
PamClientSession:: ~PamClientSession()
|
||||
PamClientSession::~PamClientSession()
|
||||
{
|
||||
sqlite3_close_v2(m_dbhandle);
|
||||
}
|
||||
@ -222,7 +233,7 @@ PamClientSession* PamClientSession::create(const PamInstance& inst)
|
||||
{
|
||||
// This handle is only used from one thread, can define no_mutex.
|
||||
sqlite3* dbhandle = NULL;
|
||||
int db_flags = SQLITE_OPEN_READONLY | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_NOMUTEX;
|
||||
int db_flags = SQLITE_OPEN_READONLY | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_NOMUTEX;
|
||||
if (sqlite3_open_v2(inst.m_dbname.c_str(), &dbhandle, db_flags, NULL) == SQLITE_OK)
|
||||
{
|
||||
sqlite3_busy_timeout(dbhandle, 1000);
|
||||
@ -232,7 +243,7 @@ PamClientSession* PamClientSession::create(const PamInstance& inst)
|
||||
MXS_ERROR("Failed to open SQLite3 handle.");
|
||||
}
|
||||
PamClientSession* rval = NULL;
|
||||
if (!dbhandle || (rval = new (std::nothrow) PamClientSession(dbhandle, inst)) == NULL)
|
||||
if (!dbhandle || (rval = new( std::nothrow) PamClientSession(dbhandle, inst)) == NULL)
|
||||
{
|
||||
sqlite3_close_v2(dbhandle);
|
||||
}
|
||||
@ -246,29 +257,32 @@ PamClientSession* PamClientSession::create(const PamInstance& inst)
|
||||
* @param session MySQL session
|
||||
* @param services_out Output for services
|
||||
*/
|
||||
void PamClientSession::get_pam_user_services(const DCB* dcb, const MYSQL_session* session,
|
||||
void PamClientSession::get_pam_user_services(const DCB* dcb,
|
||||
const MYSQL_session* session,
|
||||
StringVector* services_out)
|
||||
{
|
||||
string services_query = string("SELECT authentication_string FROM ") + m_instance.m_tablename +
|
||||
" WHERE " + FIELD_USER + " = '" + session->user + "' AND '" + dcb->remote +
|
||||
"' LIKE " + FIELD_HOST + " AND (" + FIELD_ANYDB + " = '1' OR '" + session->db +
|
||||
"' = '' OR '" + session->db + "' LIKE " + FIELD_DB +
|
||||
") ORDER BY authentication_string;";
|
||||
string services_query = string("SELECT authentication_string FROM ") + m_instance.m_tablename
|
||||
+ " WHERE " + FIELD_USER + " = '" + session->user + "' AND '" + dcb->remote
|
||||
+ "' LIKE " + FIELD_HOST + " AND (" + FIELD_ANYDB + " = '1' OR '" + session->db
|
||||
+ "' = '' OR '" + session->db + "' LIKE " + FIELD_DB
|
||||
+ ") ORDER BY authentication_string;";
|
||||
MXS_DEBUG("PAM services search sql: '%s'.", services_query.c_str());
|
||||
char *err;
|
||||
char* err;
|
||||
if (sqlite3_exec(m_dbhandle, services_query.c_str(), user_services_cb, services_out, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to execute query: '%s'", err);
|
||||
sqlite3_free(err);
|
||||
}
|
||||
MXS_DEBUG("User '%s' matched %lu rows in %s db.", session->user,
|
||||
services_out->size(), m_instance.m_tablename.c_str());
|
||||
MXS_DEBUG("User '%s' matched %lu rows in %s db.",
|
||||
session->user,
|
||||
services_out->size(),
|
||||
m_instance.m_tablename.c_str());
|
||||
|
||||
if (services_out->empty())
|
||||
{
|
||||
// No service found for user with correct username & password. Check if anonymous user exists.
|
||||
const string anon_query = string("SELECT authentication_string FROM ") + m_instance.m_tablename +
|
||||
" WHERE " + FIELD_USER + " = '' AND " + FIELD_HOST + " = '%';";
|
||||
const string anon_query = string("SELECT authentication_string FROM ") + m_instance.m_tablename
|
||||
+ " WHERE " + FIELD_USER + " = '' AND " + FIELD_HOST + " = '%';";
|
||||
if (sqlite3_exec(m_dbhandle, anon_query.c_str(), user_services_cb, services_out, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to execute query: '%s'", err);
|
||||
@ -286,7 +300,8 @@ void PamClientSession::get_pam_user_services(const DCB* dcb, const MYSQL_session
|
||||
* This obviously only works with the basic password authentication scheme.
|
||||
*
|
||||
* @return Allocated packet
|
||||
* @see https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest
|
||||
* @see
|
||||
*https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest
|
||||
*/
|
||||
Buffer PamClientSession::create_auth_change_packet() const
|
||||
{
|
||||
@ -305,11 +320,11 @@ Buffer PamClientSession::create_auth_change_packet() const
|
||||
gw_mysql_set_byte3(pData, plen);
|
||||
pData += 3;
|
||||
*pData++ = m_sequence;
|
||||
*pData++ = 0xfe; // AuthSwitchRequest command
|
||||
*pData++ = 0xfe; // AuthSwitchRequest command
|
||||
memcpy(pData, DIALOG.c_str(), DIALOG_SIZE); // Plugin name
|
||||
pData += DIALOG_SIZE;
|
||||
*pData++ = DIALOG_ECHO_DISABLED;
|
||||
memcpy(pData, PASSWORD.c_str(), PASSWORD.length()); // First message
|
||||
memcpy(pData, PASSWORD.c_str(), PASSWORD.length()); // First message
|
||||
|
||||
Buffer buffer(bufdata, buflen);
|
||||
return buffer;
|
||||
@ -318,7 +333,7 @@ Buffer PamClientSession::create_auth_change_packet() const
|
||||
int PamClientSession::authenticate(DCB* dcb)
|
||||
{
|
||||
int rval = ssl_authenticate_check_status(dcb);
|
||||
MYSQL_session *ses = static_cast<MYSQL_session*>(dcb->data);
|
||||
MYSQL_session* ses = static_cast<MYSQL_session*>(dcb->data);
|
||||
if (rval == MXS_AUTH_SSL_COMPLETE && *ses->user)
|
||||
{
|
||||
rval = MXS_AUTH_FAILED;
|
||||
@ -391,7 +406,7 @@ int PamClientSession::authenticate(DCB* dcb)
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool PamClientSession::extract(DCB *dcb, GWBUF *buffer)
|
||||
bool PamClientSession::extract(DCB* dcb, GWBUF* buffer)
|
||||
{
|
||||
gwbuf_copy_data(buffer, MYSQL_SEQ_OFFSET, 1, &m_sequence);
|
||||
m_sequence++;
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "pam_auth.hh"
|
||||
|
||||
#include <stdint.h>
|
||||
@ -29,18 +29,17 @@ public:
|
||||
typedef std::vector<std::string> StringVector;
|
||||
static PamClientSession* create(const PamInstance& inst);
|
||||
~PamClientSession();
|
||||
int authenticate(DCB* client);
|
||||
bool extract(DCB *dcb, GWBUF *read_buffer);
|
||||
int authenticate(DCB* client);
|
||||
bool extract(DCB* dcb, GWBUF* read_buffer);
|
||||
private:
|
||||
PamClientSession(sqlite3* dbhandle, const PamInstance& instance);
|
||||
void get_pam_user_services(const DCB* dcb, const MYSQL_session* session,
|
||||
void get_pam_user_services(const DCB* dcb,
|
||||
const MYSQL_session* session,
|
||||
StringVector* services_out);
|
||||
maxscale::Buffer create_auth_change_packet() const;
|
||||
|
||||
pam_auth_state m_state; /**< Authentication state*/
|
||||
uint8_t m_sequence; /**< The next packet seqence number */
|
||||
sqlite3* const m_dbhandle; /**< SQLite3 database handle */
|
||||
const PamInstance& m_instance; /**< Authenticator instance */
|
||||
pam_auth_state m_state; /**< Authentication state*/
|
||||
uint8_t m_sequence; /**< The next packet seqence number */
|
||||
sqlite3* const m_dbhandle; /**< SQLite3 database handle */
|
||||
const PamInstance& m_instance; /**< Authenticator instance */
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
#include <maxscale/mysql_utils.h>
|
||||
|
||||
#define DEFAULT_PAM_DATABASE_NAME "file:pam.db?mode=memory&cache=shared"
|
||||
#define DEFAULT_PAM_TABLE_NAME "pam_users"
|
||||
#define DEFAULT_PAM_TABLE_NAME "pam_users"
|
||||
using std::string;
|
||||
|
||||
/**
|
||||
@ -30,17 +30,17 @@ using std::string;
|
||||
* @param options Listener options
|
||||
* @return New client authenticator instance or NULL on error
|
||||
*/
|
||||
PamInstance* PamInstance::create(char **options)
|
||||
PamInstance* PamInstance::create(char** options)
|
||||
{
|
||||
/** Name of the in-memory database */
|
||||
const string pam_db_name = DEFAULT_PAM_DATABASE_NAME;
|
||||
/** The table name where we store the users */
|
||||
const string pam_table_name = DEFAULT_PAM_TABLE_NAME;
|
||||
/** CREATE TABLE statement for the in-memory table */
|
||||
const string create_sql = string("CREATE TABLE IF NOT EXISTS ") + pam_table_name +
|
||||
" (" + FIELD_USER + " varchar(255), " + FIELD_HOST + " varchar(255), " +
|
||||
FIELD_DB + " varchar(255), " + FIELD_ANYDB + " boolean, " +
|
||||
FIELD_AUTHSTR + " text);";
|
||||
const string create_sql = string("CREATE TABLE IF NOT EXISTS ") + pam_table_name
|
||||
+ " (" + FIELD_USER + " varchar(255), " + FIELD_HOST + " varchar(255), "
|
||||
+ FIELD_DB + " varchar(255), " + FIELD_ANYDB + " boolean, "
|
||||
+ FIELD_AUTHSTR + " text);";
|
||||
if (sqlite3_threadsafe() == 0)
|
||||
{
|
||||
MXS_WARNING("SQLite3 was compiled with thread safety off. May cause "
|
||||
@ -49,15 +49,15 @@ PamInstance* PamInstance::create(char **options)
|
||||
bool error = false;
|
||||
/* This handle may be used from multiple threads, set full mutex. */
|
||||
sqlite3* dbhandle = NULL;
|
||||
int db_flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
|
||||
SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_FULLMUTEX;
|
||||
int db_flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
|
||||
| SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_FULLMUTEX;
|
||||
if (sqlite3_open_v2(pam_db_name.c_str(), &dbhandle, db_flags, NULL) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to open SQLite3 handle.");
|
||||
error = true;
|
||||
}
|
||||
|
||||
char *err;
|
||||
char* err;
|
||||
if (!error && sqlite3_exec(dbhandle, create_sql.c_str(), NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to create database: '%s'", err);
|
||||
@ -65,9 +65,9 @@ PamInstance* PamInstance::create(char **options)
|
||||
error = true;
|
||||
}
|
||||
|
||||
PamInstance *instance = NULL;
|
||||
if (!error &&
|
||||
((instance = new (std::nothrow) PamInstance(dbhandle, pam_db_name, pam_table_name)) == NULL))
|
||||
PamInstance* instance = NULL;
|
||||
if (!error
|
||||
&& ((instance = new( std::nothrow) PamInstance(dbhandle, pam_db_name, pam_table_name)) == NULL))
|
||||
{
|
||||
sqlite3_close_v2(dbhandle);
|
||||
}
|
||||
@ -97,8 +97,11 @@ PamInstance::PamInstance(sqlite3* dbhandle, const string& dbname, const string&
|
||||
* @param anydb Global access to databases
|
||||
* @param pam_service The PAM service used
|
||||
*/
|
||||
void PamInstance::add_pam_user(const char *user, const char *host,
|
||||
const char *db, bool anydb, const char *pam_service)
|
||||
void PamInstance::add_pam_user(const char* user,
|
||||
const char* host,
|
||||
const char* db,
|
||||
bool anydb,
|
||||
const char* pam_service)
|
||||
{
|
||||
/**
|
||||
* The insert query template which adds users to the pam_users table.
|
||||
@ -106,8 +109,8 @@ void PamInstance::add_pam_user(const char *user, const char *host,
|
||||
* Note that 'db' and 'pam_service' are strings that can be NULL and thus they have
|
||||
* no quotes around them. The quotes for strings are added in this function.
|
||||
*/
|
||||
const string insert_sql_template =
|
||||
"INSERT INTO " + m_tablename + " VALUES ('%s', '%s', %s, '%s', %s)";
|
||||
const string insert_sql_template
|
||||
= "INSERT INTO " + m_tablename + " VALUES ('%s', '%s', %s, '%s', %s)";
|
||||
|
||||
/** Used for NULL value creation in the INSERT query */
|
||||
const char NULL_TOKEN[] = "NULL";
|
||||
@ -132,14 +135,19 @@ void PamInstance::add_pam_user(const char *user, const char *host,
|
||||
service_str = NULL_TOKEN;
|
||||
}
|
||||
|
||||
size_t len = insert_sql_template.length() + strlen(user) + strlen(host) + db_str.length() +
|
||||
service_str.length() + 1;
|
||||
size_t len = insert_sql_template.length() + strlen(user) + strlen(host) + db_str.length()
|
||||
+ service_str.length() + 1;
|
||||
|
||||
char insert_sql[len + 1];
|
||||
sprintf(insert_sql, insert_sql_template.c_str(), user, host, db_str.c_str(),
|
||||
anydb ? "1" : "0", service_str.c_str());
|
||||
sprintf(insert_sql,
|
||||
insert_sql_template.c_str(),
|
||||
user,
|
||||
host,
|
||||
db_str.c_str(),
|
||||
anydb ? "1" : "0",
|
||||
service_str.c_str());
|
||||
|
||||
char *err;
|
||||
char* err;
|
||||
if (sqlite3_exec(m_dbhandle, insert_sql, NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to insert user: %s", err);
|
||||
@ -154,7 +162,7 @@ void PamInstance::delete_old_users()
|
||||
{
|
||||
/** Delete query used to clean up the database before loading new users */
|
||||
const string delete_query = "DELETE FROM " + m_tablename;
|
||||
char *err;
|
||||
char* err;
|
||||
if (sqlite3_exec(m_dbhandle, delete_query.c_str(), NULL, NULL, &err) != SQLITE_OK)
|
||||
{
|
||||
MXS_ERROR("Failed to delete old users: %s", err);
|
||||
@ -172,17 +180,17 @@ void PamInstance::delete_old_users()
|
||||
int PamInstance::load_users(SERVICE* service)
|
||||
{
|
||||
/** Query that gets all users that authenticate via the pam plugin */
|
||||
const char PAM_USERS_QUERY[] =
|
||||
"SELECT u.user, u.host, d.db, u.select_priv, u.authentication_string FROM "
|
||||
"mysql.user AS u LEFT JOIN mysql.db AS d ON (u.user = d.user AND u.host = d.host) WHERE "
|
||||
"(u.plugin = 'pam' AND (d.db IS NOT NULL OR u.select_priv = 'Y')) "
|
||||
"UNION "
|
||||
"SELECT u.user, u.host, t.db, u.select_priv, u.authentication_string FROM "
|
||||
"mysql.user AS u LEFT JOIN mysql.tables_priv AS t ON (u.user = t.user AND u.host = t.host) WHERE "
|
||||
"(u.plugin = 'pam' AND t.db IS NOT NULL AND u.select_priv = 'N') "
|
||||
"ORDER BY user";
|
||||
#if defined(SS_DEBUG)
|
||||
const unsigned int PAM_USERS_QUERY_NUM_FIELDS = 5;
|
||||
const char PAM_USERS_QUERY[]
|
||||
= "SELECT u.user, u.host, d.db, u.select_priv, u.authentication_string FROM "
|
||||
"mysql.user AS u LEFT JOIN mysql.db AS d ON (u.user = d.user AND u.host = d.host) WHERE "
|
||||
"(u.plugin = 'pam' AND (d.db IS NOT NULL OR u.select_priv = 'Y')) "
|
||||
"UNION "
|
||||
"SELECT u.user, u.host, t.db, u.select_priv, u.authentication_string FROM "
|
||||
"mysql.user AS u LEFT JOIN mysql.tables_priv AS t ON (u.user = t.user AND u.host = t.host) WHERE "
|
||||
"(u.plugin = 'pam' AND t.db IS NOT NULL AND u.select_priv = 'N') "
|
||||
"ORDER BY user";
|
||||
#if defined (SS_DEBUG)
|
||||
const unsigned int PAM_USERS_QUERY_NUM_FIELDS = 5;
|
||||
#endif
|
||||
|
||||
const char* user;
|
||||
@ -193,29 +201,33 @@ int PamInstance::load_users(SERVICE* service)
|
||||
|
||||
if ((pw = decrypt_password(password)))
|
||||
{
|
||||
for (SERVER_REF *servers = service->dbref; servers; servers = servers->next)
|
||||
for (SERVER_REF* servers = service->dbref; servers; servers = servers->next)
|
||||
{
|
||||
MYSQL *mysql = mysql_init(NULL);
|
||||
MYSQL* mysql = mysql_init(NULL);
|
||||
if (mxs_mysql_real_connect(mysql, servers->server, user, pw))
|
||||
{
|
||||
if (mysql_query(mysql, PAM_USERS_QUERY))
|
||||
{
|
||||
MXS_ERROR("Failed to query server '%s' for PAM users: '%s'.",
|
||||
servers->server->name, mysql_error(mysql));
|
||||
servers->server->name,
|
||||
mysql_error(mysql));
|
||||
}
|
||||
else
|
||||
{
|
||||
MYSQL_RES *res = mysql_store_result(mysql);
|
||||
MYSQL_RES* res = mysql_store_result(mysql);
|
||||
delete_old_users();
|
||||
if (res)
|
||||
{
|
||||
mxb_assert(mysql_num_fields(res) == PAM_USERS_QUERY_NUM_FIELDS);
|
||||
MXS_NOTICE("Loaded %llu users for service %s.", mysql_num_rows(res),
|
||||
MXS_NOTICE("Loaded %llu users for service %s.",
|
||||
mysql_num_rows(res),
|
||||
service->name);
|
||||
MYSQL_ROW row;
|
||||
while ((row = mysql_fetch_row(res)))
|
||||
{
|
||||
add_pam_user(row[0], row[1], row[2],
|
||||
add_pam_user(row[0],
|
||||
row[1],
|
||||
row[2],
|
||||
row[3] && strcasecmp(row[3], "Y") == 0,
|
||||
row[4]);
|
||||
}
|
||||
@ -267,7 +279,7 @@ void PamInstance::diagnostic(DCB* dcb)
|
||||
json_decref(array);
|
||||
}
|
||||
|
||||
static int diag_cb_json(void *data, int columns, char **row, char **field_names)
|
||||
static int diag_cb_json(void* data, int columns, char** row, char** field_names)
|
||||
{
|
||||
mxb_assert(columns == NUM_FIELDS);
|
||||
json_t* obj = json_object();
|
||||
@ -283,7 +295,7 @@ static int diag_cb_json(void *data, int columns, char **row, char **field_names)
|
||||
json_t* PamInstance::diagnostic_json()
|
||||
{
|
||||
json_t* rval = json_array();
|
||||
char *err;
|
||||
char* err;
|
||||
string select = "SELECT * FROM " + m_tablename + ";";
|
||||
if (sqlite3_exec(m_dbhandle, select.c_str(), diag_cb_json, rval, &err) != SQLITE_OK)
|
||||
{
|
||||
@ -308,12 +320,13 @@ bool PamInstance::query_anon_proxy_user(SERVER* server, MYSQL* conn)
|
||||
if (mysql_query(conn, ANON_USER_QUERY))
|
||||
{
|
||||
MXS_ERROR("Failed to query server '%s' for the anonymous PAM user: '%s'.",
|
||||
server->name, mysql_error(conn));
|
||||
server->name,
|
||||
mysql_error(conn));
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MYSQL_RES *res = mysql_store_result(conn);
|
||||
MYSQL_RES* res = mysql_store_result(conn);
|
||||
if (res)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(res);
|
||||
@ -334,7 +347,8 @@ bool PamInstance::query_anon_proxy_user(SERVER* server, MYSQL* conn)
|
||||
if (mysql_query(conn, ANON_GRANT_QUERY))
|
||||
{
|
||||
MXS_ERROR("Failed to query server '%s' for the grants of the anonymous PAM user: '%s'.",
|
||||
server->name, mysql_error(conn));
|
||||
server->name,
|
||||
mysql_error(conn));
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "pam_auth.hh"
|
||||
|
||||
#include <string>
|
||||
@ -23,22 +23,23 @@ class PamInstance
|
||||
PamInstance(const PamInstance& orig);
|
||||
PamInstance& operator=(const PamInstance&);
|
||||
public:
|
||||
static PamInstance* create(char **options);
|
||||
static PamInstance* create(char** options);
|
||||
~PamInstance();
|
||||
int load_users(SERVICE* service);
|
||||
void diagnostic(DCB* dcb);
|
||||
int load_users(SERVICE* service);
|
||||
void diagnostic(DCB* dcb);
|
||||
json_t* diagnostic_json();
|
||||
|
||||
const std::string m_dbname; /**< Name of the in-memory database */
|
||||
const std::string m_tablename; /**< The table where users are stored */
|
||||
const std::string m_dbname; /**< Name of the in-memory database */
|
||||
const std::string m_tablename; /**< The table where users are stored */
|
||||
private:
|
||||
PamInstance(sqlite3* dbhandle, const std::string& m_dbname, const std::string& tablename);
|
||||
void add_pam_user(const char *user, const char *host, const char *db, bool anydb,
|
||||
const char *pam_service);
|
||||
void add_pam_user(const char* user,
|
||||
const char* host,
|
||||
const char* db,
|
||||
bool anydb,
|
||||
const char* pam_service);
|
||||
void delete_old_users();
|
||||
bool query_anon_proxy_user(SERVER* server, MYSQL* conn);
|
||||
|
||||
sqlite3 * const m_dbhandle; /**< SQLite3 database handle */
|
||||
sqlite3* const m_dbhandle; /**< SQLite3 database handle */
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -19,13 +19,13 @@
|
||||
#include "pam_backend_session.hh"
|
||||
#include "../pam_auth_common.hh"
|
||||
|
||||
static void* pam_backend_auth_alloc(void *instance)
|
||||
static void* pam_backend_auth_alloc(void* instance)
|
||||
{
|
||||
PamBackendSession* pses = new (std::nothrow) PamBackendSession();
|
||||
PamBackendSession* pses = new( std::nothrow) PamBackendSession();
|
||||
return pses;
|
||||
}
|
||||
|
||||
static void pam_backend_auth_free(void *data)
|
||||
static void pam_backend_auth_free(void* data)
|
||||
{
|
||||
delete static_cast<PamBackendSession*>(data);
|
||||
}
|
||||
@ -39,9 +39,9 @@ static void pam_backend_auth_free(void *data)
|
||||
* @return MXS_AUTH_INCOMPLETE if authentication is ongoing, MXS_AUTH_SUCCEEDED
|
||||
* if authentication is complete and MXS_AUTH_FAILED if authentication failed.
|
||||
*/
|
||||
static bool pam_backend_auth_extract(DCB *dcb, GWBUF *buffer)
|
||||
static bool pam_backend_auth_extract(DCB* dcb, GWBUF* buffer)
|
||||
{
|
||||
PamBackendSession *pses = static_cast<PamBackendSession*>(dcb->authenticator_data);
|
||||
PamBackendSession* pses = static_cast<PamBackendSession*>(dcb->authenticator_data);
|
||||
return pses->extract(dcb, buffer);
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ static bool pam_backend_auth_extract(DCB *dcb, GWBUF *buffer)
|
||||
*
|
||||
* @return True if DCB supports SSL
|
||||
*/
|
||||
static bool pam_backend_auth_connectssl(DCB *dcb)
|
||||
static bool pam_backend_auth_connectssl(DCB* dcb)
|
||||
{
|
||||
return dcb->server->server_ssl != NULL;
|
||||
}
|
||||
@ -65,9 +65,9 @@ static bool pam_backend_auth_connectssl(DCB *dcb)
|
||||
* @return MXS_AUTH_INCOMPLETE if authentication is ongoing, MXS_AUTH_SUCCEEDED
|
||||
* if authentication is complete and MXS_AUTH_FAILED if authentication failed.
|
||||
*/
|
||||
static int pam_backend_auth_authenticate(DCB *dcb)
|
||||
static int pam_backend_auth_authenticate(DCB* dcb)
|
||||
{
|
||||
PamBackendSession *pses = static_cast<PamBackendSession*>(dcb->authenticator_data);
|
||||
PamBackendSession* pses = static_cast<PamBackendSession*>(dcb->authenticator_data);
|
||||
return pses->authenticate(dcb);
|
||||
}
|
||||
|
||||
@ -76,40 +76,39 @@ extern "C"
|
||||
/**
|
||||
* Module handle entry point
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
pam_backend_auth_alloc, /* Allocate authenticator data */
|
||||
pam_backend_auth_extract, /* Extract data into structure */
|
||||
pam_backend_auth_connectssl, /* Check if client supports SSL */
|
||||
pam_backend_auth_authenticate, /* Authenticate user credentials */
|
||||
NULL, /* Client plugin will free shared data */
|
||||
pam_backend_auth_free, /* Free authenticator data */
|
||||
NULL, /* Load users from backend databases */
|
||||
NULL, /* No diagnostic */
|
||||
NULL,
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
static MXS_AUTHENTICATOR MyObject =
|
||||
{
|
||||
NULL, /* No initialize entry point */
|
||||
pam_backend_auth_alloc, /* Allocate authenticator data */
|
||||
pam_backend_auth_extract, /* Extract data into structure */
|
||||
pam_backend_auth_connectssl, /* Check if client supports SSL */
|
||||
pam_backend_auth_authenticate, /* Authenticate user credentials */
|
||||
NULL, /* Client plugin will free shared data */
|
||||
pam_backend_auth_free, /* Free authenticator data */
|
||||
NULL, /* Load users from backend databases */
|
||||
NULL, /* No diagnostic */
|
||||
NULL,
|
||||
NULL /* No user reauthentication */
|
||||
};
|
||||
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_ALPHA_RELEASE,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"PAM backend authenticator",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{ { MXS_END_MODULE_PARAMS} }
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
static MXS_MODULE info =
|
||||
{
|
||||
MXS_MODULE_API_AUTHENTICATOR,
|
||||
MXS_MODULE_ALPHA_RELEASE,
|
||||
MXS_AUTHENTICATOR_VERSION,
|
||||
"PAM backend authenticator",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
&MyObject,
|
||||
NULL, /* Process init. */
|
||||
NULL, /* Process finish. */
|
||||
NULL, /* Thread init. */
|
||||
NULL, /* Thread finish. */
|
||||
{{MXS_END_MODULE_PARAMS}}
|
||||
};
|
||||
|
||||
return &info;
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Common definitions and includes for PAMBackendAuth
|
||||
|
||||
@ -17,14 +17,14 @@
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* Check that the AuthSwitchRequest packet is as expected. Inverse of
|
||||
* create_auth_change_packet() in pam_auth.cc.
|
||||
*
|
||||
* @param dcb Backend DCB
|
||||
* @param buffer Buffer containing an AuthSwitchRequest packet
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
bool check_auth_switch_request(DCB *dcb, GWBUF *buffer)
|
||||
* Check that the AuthSwitchRequest packet is as expected. Inverse of
|
||||
* create_auth_change_packet() in pam_auth.cc.
|
||||
*
|
||||
* @param dcb Backend DCB
|
||||
* @param buffer Buffer containing an AuthSwitchRequest packet
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
bool check_auth_switch_request(DCB* dcb, GWBUF* buffer)
|
||||
{
|
||||
/**
|
||||
* The AuthSwitchRequest packet:
|
||||
@ -45,19 +45,22 @@ bool check_auth_switch_request(DCB *dcb, GWBUF *buffer)
|
||||
/** Server responded with something we did not expect. If it's an OK packet,
|
||||
* it's possible that the server authenticated us as the anonymous user. This
|
||||
* means that the server is not secure. */
|
||||
bool was_ok_packet = copied > MYSQL_HEADER_LEN &&
|
||||
data[MYSQL_HEADER_LEN + 1] == MYSQL_REPLY_OK;
|
||||
bool was_ok_packet = copied > MYSQL_HEADER_LEN
|
||||
&& data[MYSQL_HEADER_LEN + 1] == MYSQL_REPLY_OK;
|
||||
MXS_ERROR("Server '%s' returned an unexpected authentication response.%s",
|
||||
dcb->server->name, was_ok_packet ?
|
||||
" Authentication was complete before it even started, "
|
||||
"anonymous users might not be disabled." : "");
|
||||
dcb->server->name,
|
||||
was_ok_packet
|
||||
? " Authentication was complete before it even started, "
|
||||
"anonymous users might not be disabled." : "");
|
||||
return false;
|
||||
}
|
||||
unsigned int buflen = gwbuf_length(buffer);
|
||||
if (buflen != expected_buflen)
|
||||
{
|
||||
MXS_ERROR("Length of server AuthSwitchRequest packet was '%u', expected '%u'. %s",
|
||||
buflen, expected_buflen, GENERAL_ERRMSG);
|
||||
buflen,
|
||||
expected_buflen,
|
||||
GENERAL_ERRMSG);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -68,9 +71,9 @@ bool check_auth_switch_request(DCB *dcb, GWBUF *buffer)
|
||||
uint8_t* msg_loc = msg_type_loc + 1;
|
||||
|
||||
bool rval = false;
|
||||
if ((DIALOG == (char*)plugin_name_loc) &&
|
||||
(msg_type == DIALOG_ECHO_ENABLED || msg_type == DIALOG_ECHO_DISABLED) &&
|
||||
PASSWORD.compare(0, PASSWORD.length(), (char*)msg_loc, PASSWORD.length()) == 0)
|
||||
if ((DIALOG == (char*)plugin_name_loc)
|
||||
&& (msg_type == DIALOG_ECHO_ENABLED || msg_type == DIALOG_ECHO_DISABLED)
|
||||
&& PASSWORD.compare(0, PASSWORD.length(), (char*)msg_loc, PASSWORD.length()) == 0)
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
@ -93,10 +96,10 @@ PamBackendSession::PamBackendSession()
|
||||
* @param dcb Backend DCB
|
||||
* @return True on success, false on error
|
||||
*/
|
||||
bool PamBackendSession::send_client_password(DCB *dcb)
|
||||
bool PamBackendSession::send_client_password(DCB* dcb)
|
||||
{
|
||||
bool rval = false;
|
||||
MYSQL_session *ses = (MYSQL_session*)dcb->session->client_dcb->data;
|
||||
MYSQL_session* ses = (MYSQL_session*)dcb->session->client_dcb->data;
|
||||
size_t buflen = MYSQL_HEADER_LEN + ses->auth_token_len;
|
||||
uint8_t bufferdata[buflen];
|
||||
gw_mysql_set_byte3(bufferdata, ses->auth_token_len);
|
||||
@ -105,7 +108,7 @@ bool PamBackendSession::send_client_password(DCB *dcb)
|
||||
return dcb_write(dcb, gwbuf_alloc_and_load(buflen, bufferdata));
|
||||
}
|
||||
|
||||
bool PamBackendSession::extract(DCB *dcb, GWBUF *buffer)
|
||||
bool PamBackendSession::extract(DCB* dcb, GWBUF* buffer)
|
||||
{
|
||||
gwbuf_copy_data(buffer, MYSQL_SEQ_OFFSET, 1, &m_sequence);
|
||||
m_sequence++;
|
||||
@ -135,12 +138,13 @@ bool PamBackendSession::extract(DCB *dcb, GWBUF *buffer)
|
||||
if (!rval)
|
||||
{
|
||||
MXS_DEBUG("pam_backend_auth_extract to backend '%s' failed for user '%s'.",
|
||||
dcb->server->name, dcb->user);
|
||||
dcb->server->name,
|
||||
dcb->user);
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
int PamBackendSession::authenticate(DCB *dcb)
|
||||
int PamBackendSession::authenticate(DCB* dcb)
|
||||
{
|
||||
int rval = MXS_AUTH_FAILED;
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "pam_backend_auth.hh"
|
||||
|
||||
#include <stdint.h>
|
||||
@ -22,12 +22,12 @@ class PamBackendSession
|
||||
PamBackendSession& operator=(const PamBackendSession&);
|
||||
public:
|
||||
PamBackendSession();
|
||||
bool extract(DCB *dcb, GWBUF *buffer);
|
||||
int authenticate(DCB *dcb);
|
||||
bool extract(DCB* dcb, GWBUF* buffer);
|
||||
int authenticate(DCB* dcb);
|
||||
|
||||
private:
|
||||
bool send_client_password(DCB *dcb);
|
||||
bool send_client_password(DCB* dcb);
|
||||
|
||||
pam_auth_state m_state; /**< Authentication state*/
|
||||
uint8_t m_sequence; /**< The next packet seqence number */
|
||||
pam_auth_state m_state; /**< Authentication state*/
|
||||
uint8_t m_sequence; /**< The next packet seqence number */
|
||||
};
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Common declarations for both PAMAuth and PAMBackendAuth
|
||||
@ -33,10 +33,9 @@ enum pam_auth_state
|
||||
};
|
||||
|
||||
/* Magic numbers from server source
|
||||
https://github.com/MariaDB/server/blob/10.2/plugin/auth_pam/auth_pam.c */
|
||||
* https://github.com/MariaDB/server/blob/10.2/plugin/auth_pam/auth_pam.c */
|
||||
enum dialog_plugin_msg_types
|
||||
{
|
||||
DIALOG_ECHO_ENABLED = 2,
|
||||
DIALOG_ECHO_ENABLED = 2,
|
||||
DIALOG_ECHO_DISABLED = 4
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user