Merge branch '2.2' into 2.2-mrm

This commit is contained in:
Johan Wikman
2017-09-26 14:31:32 +03:00
15 changed files with 398 additions and 52 deletions

View File

@ -594,6 +594,11 @@ documentation for more details.
Enable or disable the admin interface. This allows the admin interface to Enable or disable the admin interface. This allows the admin interface to
be completely disabled to prevent access to it. be completely disabled to prevent access to it.
#### `admin_log_auth_failures`
Log authentication failures for the admin interface. This parameter expects a
boolean value and is enabled by default.
#### `sql_mode` #### `sql_mode`
Specifies whether the query classifier parser should initially expect _MariaDB_ Specifies whether the query classifier parser should initially expect _MariaDB_

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. 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_MAJOR "2" CACHE STRING "Major version")
set(MAXSCALE_VERSION_MINOR "1" CACHE STRING "Minor 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 # This should only be incremented if a package is rebuilt
set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number") 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); int gw_getsockerrno(int fd);
char *create_hex_sha1_sha1_passwd(char *passwd); 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); char* trim(char *str);
void replace_whitespace(char* str); void replace_whitespace(char* str);
char* squeeze_whitespace(char* str); char* squeeze_whitespace(char* str);
bool strip_escape_chars(char*); bool strip_escape_chars(char*);

View File

@ -34,7 +34,8 @@ const maxscale_params = [
'auth_connect_timeout', 'auth_connect_timeout',
'auth_read_timeout', 'auth_read_timeout',
'auth_write_timeout', 'auth_write_timeout',
'admin_auth' 'admin_auth',
'admin_log_auth_failures'
] ]
exports.command = 'alter <command>' exports.command = 'alter <command>'

View File

@ -490,6 +490,10 @@ add_test_executable(mxs1418.cpp mxs1418 replication LABELS maxscale REPL_BACKEND
# https://jira.mariadb.org/browse/MXS-1295 # https://jira.mariadb.org/browse/MXS-1295
add_test_executable(mxs1295_sp_call.cpp mxs1295_sp_call mxs1295 LABELS maxscale REPL_BACKEND) 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 # 'namedserverfilter' test
add_test_executable(namedserverfilter.cpp namedserverfilter namedserverfilter LABELS namedserverfilter LIGHT REPL_BACKEND) 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_trxcompare testtrxcompare.cc ../../../query_classifier/test/testreader.cc)
add_executable(test_trxtracking testtrxtracking.cc) add_executable(test_trxtracking testtrxtracking.cc)
add_executable(test_users testusers.cc) add_executable(test_users testusers.cc)
add_executable(test_utils testutils.cc)
add_executable(testmaxscalepcre2 testmaxscalepcre2.cc) add_executable(testmaxscalepcre2 testmaxscalepcre2.cc)
add_executable(testmodulecmd testmodulecmd.cc) add_executable(testmodulecmd testmodulecmd.cc)
add_executable(testconfig testconfig.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_trxcompare maxscale-common)
target_link_libraries(test_trxtracking maxscale-common) target_link_libraries(test_trxtracking maxscale-common)
target_link_libraries(test_users 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(testmaxscalepcre2 maxscale-common)
target_link_libraries(testmodulecmd maxscale-common) target_link_libraries(testmodulecmd maxscale-common)
target_link_libraries(testconfig maxscale-common) target_link_libraries(testconfig maxscale-common)
@ -70,6 +72,7 @@ add_test(TestServer test_server)
add_test(TestService test_service) add_test(TestService test_service)
add_test(TestSpinlock test_spinlock) add_test(TestSpinlock test_spinlock)
add_test(TestUsers test_users) add_test(TestUsers test_users)
add_test(TestUtils test_utils)
add_test(TestModulecmd testmodulecmd) add_test(TestModulecmd testmodulecmd)
add_test(TestConfig testconfig) add_test(TestConfig testconfig)
add_test(TestTrxTracking test_trxtracking) 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); return mkdir_all_internal(local_path, (mode_t)mask);
} }
/** char* trim_leading(char* str)
* Trim leading and trailing whitespace from a string {
* char* ptr = str;
* @param str String to trim
* @return Trimmed string while (isspace(*ptr))
*/ {
char* trim(char *str) ptr++;
}
if (ptr != str)
{
memmove(str, ptr, strlen(ptr) + 1);
}
return str;
}
char* trim_trailing(char* str)
{ {
char* ptr = strchr(str, '\0') - 1; char* ptr = strchr(str, '\0') - 1;
@ -472,21 +483,14 @@ char* trim(char *str)
*(ptr + 1) = '\0'; *(ptr + 1) = '\0';
} }
ptr = str;
while (isspace(*ptr))
{
ptr++;
}
if (ptr != str)
{
memmove(str, ptr, strlen(ptr) + 1);
}
return str; return str;
} }
char* trim(char *str)
{
return trim_leading(trim_trailing(str));
}
/** /**
* @brief Replace whitespace with hyphens * @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; 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) uint8_t *scramble, size_t scramble_len)
{ {
sqlite3 *handle = instance->handle;
size_t len = sizeof(mysqlauth_validate_user_query) + strlen(session->user) * 2 + 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; strlen(session->db) * 2 + MYSQL_HOST_MAXLEN + session->auth_token_len * 4 + 1;
char sql[len + 1]; char sql[len + 1];
int rval = MXS_AUTH_FAILED; int rval = MXS_AUTH_FAILED;
char *err; char *err;
sprintf(sql, mysqlauth_validate_user_query, session->user, dcb->remote, if (instance->skip_auth)
dcb->remote, session->db, session->db); {
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 = {}; 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; MYSQL_AUTH *instance = (MYSQL_AUTH*)dcb->listener->auth_instance;
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol); 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)); protocol->scramble, sizeof(protocol->scramble));
if (auth_ret != MXS_AUTH_SUCCEEDED && if (auth_ret != MXS_AUTH_SUCCEEDED &&
!instance->skip_auth &&
service_refresh_users(dcb->service) == 0) 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)); protocol->scramble, sizeof(protocol->scramble));
} }
/* on successful authentication, set user into dcb field */ /* 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; auth_ret = MXS_AUTH_SUCCEEDED;
dcb->user = MXS_STRDUP_A(client_data->user); 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; 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->handle, dcb, &temp, scramble, scramble_len); int rc = validate_mysql_user(instance, dcb, &temp, scramble, scramble_len);
if (rc == MXS_AUTH_SUCCEEDED) 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)" " WHERE user = '%s' AND ( '%s' = host OR '%s' LIKE host) AND (anydb = '1' OR '%s' = '' OR '%s' LIKE db)"
" LIMIT 1"; " 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 */ /** Query that checks that the database exists */
static const char mysqlauth_validate_database_query[] = static const char mysqlauth_validate_database_query[] =
"SELECT * FROM " MYSQLAUTH_DATABASES_TABLE_NAME " WHERE db = '%s' LIMIT 1"; "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 * @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 dcb Client DCB
* @param session Shared MySQL session * @param session Shared MySQL session
* @param scramble The scramble sent to the client in the initial handshake * @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 * @return MXS_AUTH_SUCCEEDED if the user has access to the database
*/ */
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); uint8_t *scramble, size_t scramble_len);
MXS_END_DECLS MXS_END_DECLS

View File

@ -27,33 +27,35 @@
*/ */
#include <maxscale/cdefs.h> #include <maxscale/cdefs.h>
#include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h>
#include <telnetd.h>
#include <sys/syslog.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 <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/housekeeper.h>
#include <maxscale/maxscale.h>
#include <maxscale/version.h>
#include <maxscale/log_manager.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 <maxscale/worker.h>
#include <debugcli.h>
#include "../../../core/maxscale/config_runtime.h" #include "../../../core/maxscale/config_runtime.h"
#include "../../../core/maxscale/maxscale.h" #include "../../../core/maxscale/maxscale.h"
#include "../../../core/maxscale/modules.h" #include "../../../core/maxscale/modules.h"
@ -1906,7 +1908,7 @@ execute_cmd(CLI_SESSION *cli)
bool in_space = false; bool in_space = false;
int nskip = 0; int nskip = 0;
args[0] = cli->cmdbuf; args[0] = trim_leading(cli->cmdbuf);
ptr = args[0]; ptr = args[0];
lptr = ptr; lptr = ptr;
i = 1; i = 1;