MXS-1842 Compile all authenticators as C++
Minimal changes, only what is needed to compile.
This commit is contained in:
578
server/modules/authenticator/CDCPlainAuth/cdc_plain_auth.cc
Normal file
578
server/modules/authenticator/CDCPlainAuth/cdc_plain_auth.cc
Normal file
@ -0,0 +1,578 @@
|
||||
/*
|
||||
* Copyright (c) 2016 MariaDB Corporation Ab
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
||||
*
|
||||
* Change Date: 2020-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2 or later of the General
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file cdc_auth.c
|
||||
*
|
||||
* CDC Authentication module for handling the checking of clients credentials
|
||||
* in the CDC protocol.
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
* Date Who Description
|
||||
* 11/03/2016 Massimiliano Pinto Initial version
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#define MXS_MODULE_NAME "CDCPlainAuth"
|
||||
|
||||
#include <maxscale/authenticator.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cdc.h>
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/modulecmd.h>
|
||||
#include <maxscale/modutil.h>
|
||||
#include <maxscale/paths.h>
|
||||
#include <maxscale/secrets.h>
|
||||
#include <maxscale/users.h>
|
||||
#include <maxscale/utils.h>
|
||||
|
||||
/* Allowed time interval (in seconds) after last update*/
|
||||
#define CDC_USERS_REFRESH_TIME 30
|
||||
/* Max number of load calls within the time interval */
|
||||
#define CDC_USERS_REFRESH_MAX_PER_TIME 4
|
||||
|
||||
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 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 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
|
||||
*
|
||||
* This function should not be called directly. The module command system will
|
||||
* call it when necessary.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
const char *user = args->argv[1].value.string;
|
||||
size_t userlen = strlen(user);
|
||||
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
|
||||
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;
|
||||
char path[PATH_MAX + 1];
|
||||
snprintf(path, PATH_MAX, "%s/%s/", get_datadir(), service->name);
|
||||
bool rval = false;
|
||||
|
||||
if (mxs_mkdir_all(path, 0777))
|
||||
{
|
||||
strcat(path, CDC_USERS_FILENAME);
|
||||
int fd = open(path, O_WRONLY | O_APPEND | O_CREAT, 0660);
|
||||
|
||||
if (fd != -1)
|
||||
{
|
||||
if (write(fd, final_data, sizeof(final_data)) == static_cast<int>(sizeof(final_data)))
|
||||
{
|
||||
MXS_NOTICE("Added user '%s' to service '%s'", user, service->name);
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
modulecmd_set_error("Failed to create directory '%s'. Read the MaxScale "
|
||||
"log for more details.", path);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
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");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function to easily call authentication check.
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @param protocol The protocol structure for the connection
|
||||
* @param username String containing username
|
||||
* @param auth_data The encrypted password for authentication
|
||||
* @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)
|
||||
{
|
||||
int rval = CDC_STATE_AUTH_FAILED;
|
||||
|
||||
if (dcb->listener->users)
|
||||
{
|
||||
/* compute SHA1 of auth_data */
|
||||
uint8_t sha1_step1[SHA_DIGEST_LENGTH] = "";
|
||||
char hex_step1[2 * SHA_DIGEST_LENGTH + 1] = "";
|
||||
|
||||
gw_sha1_str(auth_data, SHA_DIGEST_LENGTH, sha1_step1);
|
||||
gw_bin2hex(hex_step1, sha1_step1, SHA_DIGEST_LENGTH);
|
||||
|
||||
if (users_auth(dcb->listener->users, username, hex_step1))
|
||||
{
|
||||
rval = CDC_STATE_AUTH_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Authenticates a CDC user who is a client to MaxScale.
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @return Authentication status
|
||||
* @note Authentication status codes are defined in cdc.h
|
||||
*/
|
||||
static int
|
||||
cdc_auth_authenticate(DCB *dcb)
|
||||
{
|
||||
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))
|
||||
{
|
||||
auth_ret = CDC_STATE_AUTH_ERR;
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* on successful authentication, set user into dcb field */
|
||||
if (CDC_STATE_AUTH_OK == auth_ret)
|
||||
{
|
||||
dcb->user = MXS_STRDUP_A(client_data->user);
|
||||
}
|
||||
else if (dcb->service->log_auth_warnings)
|
||||
{
|
||||
MXS_NOTICE("%s: login attempt for user '%s', authentication failed.",
|
||||
dcb->service->name, client_data->user);
|
||||
}
|
||||
}
|
||||
|
||||
return auth_ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transfer data from the authentication request to the DCB.
|
||||
*
|
||||
* The request handler DCB has a field called data that contains protocol
|
||||
* specific information. This function examines a buffer containing CDC
|
||||
* authentication data and puts it into a structure that is referred to
|
||||
* by the DCB. If the information in the buffer is invalid, then a failure
|
||||
* code is returned. A call to cdc_auth_set_client_data does the
|
||||
* detailed work.
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
* @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)
|
||||
{
|
||||
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);
|
||||
CHK_PROTOCOL(protocol);
|
||||
if (dcb->data == NULL)
|
||||
{
|
||||
if (NULL == (client_data = (CDC_session *)MXS_CALLOC(1, sizeof(CDC_session))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
dcb->data = client_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
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,
|
||||
client_auth_packet_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transfer detailed data from the authentication request to the DCB.
|
||||
*
|
||||
* The caller has created the data structure pointed to by the DCB, and this
|
||||
* function fills in the details. If problems are found with the data, the
|
||||
* return code indicates failure.
|
||||
*
|
||||
* @param client_data The data structure for the DCB
|
||||
* @param protocol The protocol structure for this connection
|
||||
* @param client_auth_packet The data from the buffer received from client
|
||||
* @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)
|
||||
{
|
||||
if (client_auth_packet_size % 2 != 0)
|
||||
{
|
||||
/** gw_hex2bin expects an even number of bytes */
|
||||
client_auth_packet_size--;
|
||||
}
|
||||
|
||||
bool rval = false;
|
||||
int decoded_size = client_auth_packet_size / 2;
|
||||
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,
|
||||
client_auth_packet_size);
|
||||
decoded_buffer[decoded_size] = '\0';
|
||||
char *tmp_ptr = strchr(decoded_buffer, ':');
|
||||
|
||||
if (tmp_ptr)
|
||||
{
|
||||
size_t user_len = tmp_ptr - decoded_buffer;
|
||||
*tmp_ptr++ = '\0';
|
||||
size_t auth_len = decoded_size - (tmp_ptr - decoded_buffer);
|
||||
|
||||
if (user_len <= CDC_USER_MAXLEN && auth_len == SHA_DIGEST_LENGTH)
|
||||
{
|
||||
strcpy(client_data->user, decoded_buffer);
|
||||
memcpy(client_data->auth_data, tmp_ptr, auth_len);
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Authentication failed, the decoded client authentication "
|
||||
"packet is malformed. Expected <username>:SHA1(<password>)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Authentication failed, client authentication packet length "
|
||||
"exceeds the maximum allowed length of %d bytes.", CDC_USER_MAXLEN);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine whether the client is SSL capable
|
||||
*
|
||||
* The authentication request from the client will indicate whether the client
|
||||
* is expecting to make an SSL connection. The information has been extracted
|
||||
* in the previous functions.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free the client data pointed to by the passed DCB.
|
||||
*
|
||||
* Currently all that is required is to free the storage pointed to by
|
||||
* dcb->data. But this is intended to be implemented as part of the
|
||||
* authentication API at which time this code will be moved into the
|
||||
* CDC authenticator. If the data structure were to become more complex
|
||||
* the mechanism would still work and be the responsibility of the authenticator.
|
||||
* The DCB should not know authenticator implementation details.
|
||||
*
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
*/
|
||||
static void
|
||||
cdc_auth_free_client_data(DCB *dcb)
|
||||
{
|
||||
MXS_FREE(dcb->data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the service user to CDC dbusers (service->users)
|
||||
* via cdc_users_alloc
|
||||
*
|
||||
* @param service The current service
|
||||
* @return 0 on success, 1 on failure
|
||||
*/
|
||||
static int
|
||||
cdc_set_service_user(SERV_LISTENER *listener)
|
||||
{
|
||||
SERVICE *service = listener->service;
|
||||
char *dpwd = NULL;
|
||||
char *newpasswd = NULL;
|
||||
char *service_user = NULL;
|
||||
char *service_passwd = NULL;
|
||||
|
||||
if (serviceGetUser(service, &service_user, &service_passwd) == 0)
|
||||
{
|
||||
MXS_ERROR("failed to get service user details for service %s",
|
||||
service->name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
dpwd = decrypt_password(service->credentials.authdata);
|
||||
|
||||
if (!dpwd)
|
||||
{
|
||||
MXS_ERROR("decrypt password failed for service user %s, service %s",
|
||||
service_user,
|
||||
service->name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
newpasswd = create_hex_sha1_sha1_passwd(dpwd);
|
||||
|
||||
if (!newpasswd)
|
||||
{
|
||||
MXS_ERROR("create hex_sha1_sha1_password failed for service user %s",
|
||||
service_user);
|
||||
|
||||
MXS_FREE(dpwd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* add service user */
|
||||
(void)users_add(listener->users, service->credentials.name, newpasswd, USER_ACCOUNT_ADMIN);
|
||||
|
||||
MXS_FREE(newpasswd);
|
||||
MXS_FREE(dpwd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the AVRO users
|
||||
*
|
||||
* @param service Current service
|
||||
* @param usersfile File with users
|
||||
* @return -1 on error or users loaded (including 0)
|
||||
*/
|
||||
|
||||
static int
|
||||
cdc_read_users(USERS *users, char *usersfile)
|
||||
{
|
||||
FILE *fp;
|
||||
int loaded = 0;
|
||||
char *avro_user;
|
||||
char *user_passwd;
|
||||
/* user maxlen ':' password hash '\n' '\0' */
|
||||
char read_buffer[CDC_USER_MAXLEN + 1 + SHA_DIGEST_LENGTH + 1 + 1];
|
||||
|
||||
int max_line_size = sizeof(read_buffer) - 1;
|
||||
|
||||
if ((fp = fopen(usersfile, "r")) == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!feof(fp))
|
||||
{
|
||||
if (fgets(read_buffer, max_line_size, fp) != NULL)
|
||||
{
|
||||
char *tmp_ptr = read_buffer;
|
||||
|
||||
if ((tmp_ptr = strchr(read_buffer, ':')) != NULL)
|
||||
{
|
||||
*tmp_ptr++ = '\0';
|
||||
avro_user = read_buffer;
|
||||
user_passwd = tmp_ptr;
|
||||
if ((tmp_ptr = strchr(user_passwd, '\n')) != NULL)
|
||||
{
|
||||
*tmp_ptr = '\0';
|
||||
}
|
||||
|
||||
/* add user */
|
||||
users_add(users, avro_user, user_passwd, USER_ACCOUNT_ADMIN);
|
||||
|
||||
loaded++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replace the user/passwd in the servicei users tbale from a db file
|
||||
*
|
||||
* @param service The current service
|
||||
*/
|
||||
int cdc_replace_users(SERV_LISTENER *listener)
|
||||
{
|
||||
int rc = MXS_AUTH_LOADUSERS_ERROR;
|
||||
USERS *newusers = users_alloc();
|
||||
|
||||
if (newusers)
|
||||
{
|
||||
char path[PATH_MAX + 1];
|
||||
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;
|
||||
|
||||
spinlock_acquire(&listener->lock);
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
/** Successfully loaded at least one user */
|
||||
oldusers = listener->users;
|
||||
listener->users = newusers;
|
||||
rc = MXS_AUTH_LOADUSERS_OK;
|
||||
}
|
||||
else if (listener->users)
|
||||
{
|
||||
/** Failed to load users, use the old users table */
|
||||
users_free(newusers);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** No existing users, use the new empty users table */
|
||||
listener->users = newusers;
|
||||
}
|
||||
|
||||
cdc_set_service_user(listener);
|
||||
|
||||
spinlock_release(&listener->lock);
|
||||
|
||||
if (oldusers)
|
||||
{
|
||||
users_free(oldusers);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
Reference in New Issue
Block a user