Merge branch '2.1' into 2.2

This commit is contained in:
Markus Mäkelä 2017-09-26 14:29:14 +03:00
commit 2633ad2fe4
13 changed files with 391 additions and 51 deletions

View File

@ -1,4 +1,4 @@
# MariaDB MaxScale 2.1.9 Release Notes
# MariaDB MaxScale 2.1.9 Release Notes -- 2017-09-25
Release 2.1.9 is a GA release.

View File

@ -5,7 +5,7 @@
set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version")
set(MAXSCALE_VERSION_MINOR "1" CACHE STRING "Minor version")
set(MAXSCALE_VERSION_PATCH "9" CACHE STRING "Patch version")
set(MAXSCALE_VERSION_PATCH "10" CACHE STRING "Patch version")
# This should only be incremented if a package is rebuilt
set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number")

View File

@ -100,8 +100,41 @@ void gw_sha1_2_str(const uint8_t *in, int in_len, const uint8_t *in2, int in2_le
int gw_getsockerrno(int fd);
char *create_hex_sha1_sha1_passwd(char *passwd);
/** String formatting functions */
/**
* Trim leading whitespace from a string.
*
* @param str String to trim.
* @return @c str
*
* @note If there is leading whitespace, the string is moved so that
* the returned pointer is always the same as the one given as
* argument.
*/
char* trim_leading(char* str);
/**
* Trim trailing whitespace from a string.
*
* @param str String to trim.
* @return @c str
*
* @note The returned pointer is always the same the one given as
* argument.
*/
char* trim_trailing(char* str);
/**
* Trim leading and trailing whitespace from a string.
*
* @param str String to trim.
* @return @c str
*
* @note If there is leading whitespace, the string is moved so that
* the returned pointer is always the same the one given as
* argument.
*/
char* trim(char *str);
void replace_whitespace(char* str);
char* squeeze_whitespace(char* str);
bool strip_escape_chars(char*);

View File

@ -490,6 +490,10 @@ add_test_executable(mxs1418.cpp mxs1418 replication LABELS maxscale REPL_BACKEND
# https://jira.mariadb.org/browse/MXS-1295
add_test_executable(mxs1295_sp_call.cpp mxs1295_sp_call mxs1295 LABELS maxscale REPL_BACKEND)
# MXS-1451: Password is not stored with skip_authentication=true
# https://jira.mariadb.org/browse/MXS-1451
add_test_executable(mxs1451_skip_auth.cpp mxs1451_skip_auth mxs1451_skip_auth LABELS maxscale REPL_BACKEND)
# 'namedserverfilter' test
add_test_executable(namedserverfilter.cpp namedserverfilter namedserverfilter LABELS namedserverfilter LIGHT REPL_BACKEND)

View File

@ -0,0 +1,89 @@
[maxscale]
threads=###threads###
[MySQL Monitor]
type=monitor
module=mysqlmon
###repl51###
servers=server1,server2,server3,server4
user=maxskysql
passwd=skysql
monitor_interval=1000
[RW Split Router]
type=service
router=readwritesplit
servers=server1,server2,server3,server4
user=maxskysql
passwd=skysql
[Read Connection Router Slave]
type=service
router=readconnroute
router_options=slave
servers=server1,server2,server3,server4
user=maxskysql
passwd=skysql
[Read Connection Router Master]
type=service
router=readconnroute
router_options=master
servers=server1,server2,server3,server4
user=maxskysql
passwd=skysql
[RW Split Listener]
type=listener
service=RW Split Router
protocol=MySQLClient
port=4006
authenticator_options=skip_authentication=true
[Read Connection Listener Slave]
type=listener
service=Read Connection Router Slave
protocol=MySQLClient
port=4009
authenticator_options=skip_authentication=true
[Read Connection Listener Master]
type=listener
service=Read Connection Router Master
protocol=MySQLClient
port=4008
authenticator_options=skip_authentication=true
[CLI]
type=service
router=cli
[CLI Listener]
type=listener
service=CLI
protocol=maxscaled
socket=default
[server1]
type=server
address=###node_server_IP_1###
port=###node_server_port_1###
protocol=MySQLBackend
[server2]
type=server
address=###node_server_IP_2###
port=###node_server_port_2###
protocol=MySQLBackend
[server3]
type=server
address=###node_server_IP_3###
port=###node_server_port_3###
protocol=MySQLBackend
[server4]
type=server
address=###node_server_IP_4###
port=###node_server_port_4###
protocol=MySQLBackend

View File

@ -0,0 +1,62 @@
/**
* MXS-1451: Password is not stored with skip_authentication=true
*
* Check that connection through MaxScale work even if authentication is disabled
*/
#include "testconnections.h"
int main(int argc, char *argv[])
{
TestConnections test(argc, argv);
test.set_timeout(60);
test.tprintf("Creating users");
test.repl->connect();
execute_query(test.repl->nodes[0], "CREATE USER 'auth_test'@'%s' IDENTIFIED BY 'test'", test.maxscale_ip());
execute_query(test.repl->nodes[0], "GRANT ALL ON *.* to 'auth_test'@'%s'", test.maxscale_ip());
execute_query(test.repl->nodes[0], "CREATE USER 'auth_test_nopw'@'%s'", test.maxscale_ip());
execute_query(test.repl->nodes[0], "GRANT ALL ON *.* to 'auth_test_nopw'@'%s'", test.maxscale_ip());
test.repl->sync_slaves();
test.repl->close_connections();
test.tprintf("Trying to connect through MaxScale");
test.set_timeout(60);
test.tprintf("... with correct credentials");
MYSQL* conn = open_conn_db(test.rwsplit_port, test.maxscale_ip(), "test", "auth_test", "test", false);
test.try_query(conn, "SHOW DATABASES");
mysql_close(conn);
test.set_timeout(60);
test.tprintf("... without a password");
conn = open_conn_db(test.rwsplit_port, test.maxscale_ip(), "test", "auth_test_nopw", "", false);
test.try_query(conn, "SHOW DATABASES");
mysql_close(conn);
test.set_timeout(60);
test.tprintf("... with wrong password");
conn = open_conn_db(test.rwsplit_port, test.maxscale_ip(), "test", "auth_test", "wrong_password", false);
test.add_result(mysql_errno(conn) == 0, "Connection with wrong password should fail");
mysql_close(conn);
test.set_timeout(60);
test.tprintf("... with a password for user without a password");
conn = open_conn_db(test.rwsplit_port, test.maxscale_ip(), "test", "auth_test_nopw", "test", false);
test.add_result(mysql_errno(conn) == 0, "Connection with wrong password to user without a password should fail");
mysql_close(conn);
test.tprintf("... with bad credentials");
conn = open_conn_db(test.rwsplit_port, test.maxscale_ip(), "test", "wrong_user", "wrong_password", false);
test.add_result(mysql_errno(conn) == 0, "Connection with bad credentials should fail");
mysql_close(conn);
test.set_timeout(60);
test.tprintf("Dropping users");
test.repl->connect();
execute_query(test.repl->nodes[0], "DROP USER 'auth_test'@'%s'", test.maxscale_ip());
execute_query(test.repl->nodes[0], "DROP USER 'auth_test_nopw'@'%s'", test.maxscale_ip());
test.repl->close_connections();
return test.global_result;
}

View File

@ -18,6 +18,7 @@ add_executable(test_spinlock testspinlock.cc)
add_executable(test_trxcompare testtrxcompare.cc ../../../query_classifier/test/testreader.cc)
add_executable(test_trxtracking testtrxtracking.cc)
add_executable(test_users testusers.cc)
add_executable(test_utils testutils.cc)
add_executable(testmaxscalepcre2 testmaxscalepcre2.cc)
add_executable(testmodulecmd testmodulecmd.cc)
add_executable(testconfig testconfig.cc)
@ -44,6 +45,7 @@ target_link_libraries(test_spinlock maxscale-common)
target_link_libraries(test_trxcompare maxscale-common)
target_link_libraries(test_trxtracking maxscale-common)
target_link_libraries(test_users maxscale-common)
target_link_libraries(test_utils maxscale-common)
target_link_libraries(testmaxscalepcre2 maxscale-common)
target_link_libraries(testmodulecmd maxscale-common)
target_link_libraries(testconfig maxscale-common)
@ -70,6 +72,7 @@ add_test(TestServer test_server)
add_test(TestService test_service)
add_test(TestSpinlock test_spinlock)
add_test(TestUsers test_users)
add_test(TestUtils test_utils)
add_test(TestModulecmd testmodulecmd)
add_test(TestConfig testconfig)
add_test(TestTrxTracking test_trxtracking)

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2019-07-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <maxscale/utils.h>
#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
namespace
{
#define TRIM_TCE(zFrom, zTo) { zFrom, zTo }
struct TRIM_TEST_CASE
{
const char* zFrom;
const char* zTo;
};
TRIM_TEST_CASE trim_testcases[] =
{
TRIM_TCE("", ""),
TRIM_TCE("a", "a"),
TRIM_TCE(" a", "a"),
TRIM_TCE("a ", "a"),
TRIM_TCE(" a ", "a"),
TRIM_TCE(" a", "a"),
TRIM_TCE("a ", "a"),
TRIM_TCE(" a ", "a"),
TRIM_TCE(" a b ", "a b"),
};
const int n_trim_testcases = sizeof(trim_testcases) / sizeof(trim_testcases[0]);
TRIM_TEST_CASE trim_leading_testcases[] =
{
TRIM_TCE("", ""),
TRIM_TCE("a", "a"),
TRIM_TCE(" a", "a"),
TRIM_TCE("a ", "a "),
TRIM_TCE(" a ", "a "),
TRIM_TCE(" a", "a"),
TRIM_TCE("a ", "a "),
TRIM_TCE(" a ", "a "),
TRIM_TCE(" a b ", "a b "),
};
const int n_trim_leading_testcases = sizeof(trim_leading_testcases) / sizeof(trim_leading_testcases[0]);
TRIM_TEST_CASE trim_trailing_testcases[] =
{
TRIM_TCE("", ""),
TRIM_TCE("a", "a"),
TRIM_TCE(" a", " a"),
TRIM_TCE("a ", "a"),
TRIM_TCE(" a ", " a"),
TRIM_TCE(" a", " a"),
TRIM_TCE("a ", "a"),
TRIM_TCE(" a ", " a"),
TRIM_TCE(" a b ", " a b"),
};
const int n_trim_trailing_testcases = sizeof(trim_trailing_testcases) / sizeof(trim_trailing_testcases[0]);
int test(TRIM_TEST_CASE* pTest_cases, int n_test_cases, char* (*p)(char*))
{
int rv = 0;
for (int i = 0; i < n_test_cases; ++i)
{
const char* zFrom = pTest_cases[i].zFrom;
const char* zTo = pTest_cases[i].zTo;
char copy[strlen(zFrom) + 1];
strcpy(copy, zFrom);
char* z = p(copy);
if (strcmp(z, zTo) != 0)
{
++rv;
}
}
return rv;
}
int test_trim()
{
cout << "trim()" << endl;
return test(trim_testcases, n_trim_testcases, trim);
}
int test_trim_leading()
{
cout << "trim_leading()" << endl;
return test(trim_leading_testcases, n_trim_leading_testcases, trim_leading);
}
int test_trim_trailing()
{
cout << "trim_trailing()" << endl;
return test(trim_trailing_testcases, n_trim_trailing_testcases, trim_trailing);
}
}
int main(int argc, char* argv[])
{
int rv = 0;
rv += test_trim();
rv += test_trim_leading();
rv += test_trim_trailing();
return rv;
}

View File

@ -452,13 +452,24 @@ bool mxs_mkdir_all(const char *path, int mask)
return mkdir_all_internal(local_path, (mode_t)mask);
}
/**
* Trim leading and trailing whitespace from a string
*
* @param str String to trim
* @return Trimmed string
*/
char* trim(char *str)
char* trim_leading(char* str)
{
char* ptr = str;
while (isspace(*ptr))
{
ptr++;
}
if (ptr != str)
{
memmove(str, ptr, strlen(ptr) + 1);
}
return str;
}
char* trim_trailing(char* str)
{
char* ptr = strchr(str, '\0') - 1;
@ -472,21 +483,14 @@ char* trim(char *str)
*(ptr + 1) = '\0';
}
ptr = str;
while (isspace(*ptr))
{
ptr++;
}
if (ptr != str)
{
memmove(str, ptr, strlen(ptr) + 1);
}
return str;
}
char* trim(char *str)
{
return trim_leading(trim_trailing(str));
}
/**
* @brief Replace whitespace with hyphens
*

View File

@ -182,17 +182,25 @@ static int auth_cb(void *data, int columns, char** rows, char** row_names)
return 0;
}
int validate_mysql_user(sqlite3 *handle, DCB *dcb, MYSQL_session *session,
int validate_mysql_user(MYSQL_AUTH* instance, DCB *dcb, MYSQL_session *session,
uint8_t *scramble, size_t scramble_len)
{
sqlite3 *handle = instance->handle;
size_t len = sizeof(mysqlauth_validate_user_query) + 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;
sprintf(sql, mysqlauth_validate_user_query, session->user, dcb->remote,
dcb->remote, session->db, session->db);
if (instance->skip_auth)
{
sprintf(sql, mysqlauth_skip_auth_query, session->user, session->db, session->db);
}
else
{
sprintf(sql, mysqlauth_validate_user_query, session->user, dcb->remote,
dcb->remote, session->db, session->db);
}
struct user_query_result res = {};

View File

@ -304,19 +304,18 @@ mysql_auth_authenticate(DCB *dcb)
MYSQL_AUTH *instance = (MYSQL_AUTH*)dcb->listener->auth_instance;
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
auth_ret = validate_mysql_user(instance->handle, dcb, client_data,
auth_ret = validate_mysql_user(instance, dcb, client_data,
protocol->scramble, sizeof(protocol->scramble));
if (auth_ret != MXS_AUTH_SUCCEEDED &&
!instance->skip_auth &&
service_refresh_users(dcb->service) == 0)
{
auth_ret = validate_mysql_user(instance->handle, dcb, client_data,
auth_ret = validate_mysql_user(instance, dcb, client_data,
protocol->scramble, sizeof(protocol->scramble));
}
/* on successful authentication, set user into dcb field */
if (auth_ret == MXS_AUTH_SUCCEEDED || instance->skip_auth)
if (auth_ret == MXS_AUTH_SUCCEEDED)
{
auth_ret = MXS_AUTH_SUCCEEDED;
dcb->user = MXS_STRDUP_A(client_data->user);
@ -638,7 +637,7 @@ int mysql_auth_reauthenticate(DCB *dcb, const char *user,
temp.auth_token_len = token_len;
MYSQL_AUTH *instance = (MYSQL_AUTH*)dcb->listener->auth_instance;
int rc = validate_mysql_user(instance->handle, dcb, &temp, scramble, scramble_len);
int rc = validate_mysql_user(instance, dcb, &temp, scramble, scramble_len);
if (rc == MXS_AUTH_SUCCEEDED)
{

View File

@ -66,6 +66,12 @@ static const char mysqlauth_validate_user_query[] =
" WHERE user = '%s' AND ( '%s' = host OR '%s' LIKE host) AND (anydb = '1' OR '%s' = '' OR '%s' LIKE 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";
/** 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";
@ -181,7 +187,7 @@ int replace_mysql_users(SERV_LISTENER *listener, bool skip_local);
/**
* @brief Verify the user has access to the database
*
* @param handle SQLite handle to MySQLAuth user database
* @param instance MySQLAuth instance
* @param dcb Client DCB
* @param session Shared MySQL session
* @param scramble The scramble sent to the client in the initial handshake
@ -189,7 +195,7 @@ 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(sqlite3 *handle, 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

View File

@ -27,33 +27,35 @@
*/
#include <maxscale/cdefs.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <telnetd.h>
#include <sys/syslog.h>
#include <telnetd.h>
#include <maxscale/alloc.h>
#include <maxscale/service.h>
#include <maxscale/router.h>
#include <maxscale/filter.h>
#include <maxscale/modulecmd.h>
#include <maxscale/atomic.h>
#include <maxscale/server.h>
#include <maxscale/spinlock.h>
#include <maxscale/buffer.h>
#include <maxscale/dcb.h>
#include <maxscale/users.h>
#include <maxscale/config.h>
#include <maxscale/adminusers.h>
#include <debugcli.h>
#include <maxscale/alloc.h>
#include <maxscale/atomic.h>
#include <maxscale/buffer.h>
#include <maxscale/config.h>
#include <maxscale/dcb.h>
#include <maxscale/filter.h>
#include <maxscale/housekeeper.h>
#include <maxscale/maxscale.h>
#include <maxscale/version.h>
#include <maxscale/log_manager.h>
#include <maxscale/maxscale.h>
#include <maxscale/modulecmd.h>
#include <maxscale/router.h>
#include <maxscale/server.h>
#include <maxscale/service.h>
#include <maxscale/spinlock.h>
#include <maxscale/users.h>
#include <maxscale/utils.h>
#include <maxscale/version.h>
#include <maxscale/worker.h>
#include <debugcli.h>
#include "../../../core/maxscale/config_runtime.h"
#include "../../../core/maxscale/maxscale.h"
#include "../../../core/maxscale/modules.h"
@ -1906,7 +1908,7 @@ execute_cmd(CLI_SESSION *cli)
bool in_space = false;
int nskip = 0;
args[0] = cli->cmdbuf;
args[0] = trim_leading(cli->cmdbuf);
ptr = args[0];
lptr = ptr;
i = 1;