From 5273cbada617e1e0214cb8c28549bda4cb8c43f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Tue, 16 Jan 2018 12:10:58 +0200 Subject: [PATCH 1/3] MXS-1600: Add case-insensitive matching to MySQLAuth The authenticator now supports similar identifier matching as the MariaDB server. The lower_case_table_names parameter explains its intended use (case-insensitive identifier matching): https://mariadb.com/kb/en/library/server-system-variables/#lower_case_table_names --- .../Authenticators/MySQL-Authenticator.md | 13 +++++++++++++ server/modules/authenticator/MySQLAuth/dbusers.c | 11 +++++++---- .../modules/authenticator/MySQLAuth/mysql_auth.c | 5 +++++ .../modules/authenticator/MySQLAuth/mysql_auth.h | 15 +++++++++++---- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/Documentation/Authenticators/MySQL-Authenticator.md b/Documentation/Authenticators/MySQL-Authenticator.md index f2397d018..ffbdb0c7d 100644 --- a/Documentation/Authenticators/MySQL-Authenticator.md +++ b/Documentation/Authenticators/MySQL-Authenticator.md @@ -67,3 +67,16 @@ injected into the list of users. ``` authenticator_options=inject_service_user=false ``` + +### `lower_case_table_names` + +Enable case-insensitive identifier matching for authentication. This parameter +is disabled by default. + +The parameter functions exactly as the MariaDB Server system variable +[lower_case_table_names](https://mariadb.com/kb/en/library/server-system-variables/#lower_case_table_names). +This makes the matching done by the authenticator on database names to be +case-insensitive by converting all names into their lowercase form. + +**Note:** The identifier names are converted using an ASCII-only function. This + means that non-ASCII characters will retain their case-sensitivity. diff --git a/server/modules/authenticator/MySQLAuth/dbusers.c b/server/modules/authenticator/MySQLAuth/dbusers.c index eb72bba95..c1981272b 100644 --- a/server/modules/authenticator/MySQLAuth/dbusers.c +++ b/server/modules/authenticator/MySQLAuth/dbusers.c @@ -186,7 +186,10 @@ 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 + + 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; @@ -198,7 +201,7 @@ int validate_mysql_user(MYSQL_AUTH* instance, DCB *dcb, MYSQL_session *session, } else { - sprintf(sql, mysqlauth_validate_user_query, session->user, dcb->remote, + sprintf(sql, validate_query, session->user, dcb->remote, dcb->remote, session->db, session->db); } @@ -214,7 +217,7 @@ int validate_mysql_user(MYSQL_AUTH* instance, DCB *dcb, MYSQL_session *session, if (!res.ok && strchr(dcb->remote, ':') && strchr(dcb->remote, '.')) { const char *ipv4 = strrchr(dcb->remote, ':') + 1; - sprintf(sql, mysqlauth_validate_user_query, session->user, ipv4, ipv4, + sprintf(sql, validate_query, session->user, ipv4, ipv4, session->db, session->db); if (sqlite3_exec(handle, sql, auth_cb, &res, &err) != SQLITE_OK) @@ -233,7 +236,7 @@ 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, mysqlauth_validate_user_query, session->user, client_hostname, + 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) diff --git a/server/modules/authenticator/MySQLAuth/mysql_auth.c b/server/modules/authenticator/MySQLAuth/mysql_auth.c index d8e4f6e17..2b9177e21 100644 --- a/server/modules/authenticator/MySQLAuth/mysql_auth.c +++ b/server/modules/authenticator/MySQLAuth/mysql_auth.c @@ -183,6 +183,7 @@ static void* mysql_auth_init(char **options) instance->cache_dir = NULL; instance->inject_service_user = true; instance->skip_auth = false; + instance->lower_case_table_names = false; instance->handle = NULL; for (int i = 0; options[i]; i++) @@ -209,6 +210,10 @@ static void* mysql_auth_init(char **options) { instance->skip_auth = config_truth_value(value); } + else if (strcmp(options[i], "lower_case_table_names") == 0) + { + instance->lower_case_table_names = config_truth_value(value); + } else { MXS_ERROR("Unknown authenticator option: %s", options[i]); diff --git a/server/modules/authenticator/MySQLAuth/mysql_auth.h b/server/modules/authenticator/MySQLAuth/mysql_auth.h index 07f66a1e5..ae289e202 100644 --- a/server/modules/authenticator/MySQLAuth/mysql_auth.h +++ b/server/modules/authenticator/MySQLAuth/mysql_auth.h @@ -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 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"; + /** Query that only checks if there's a matching user */ static const char mysqlauth_skip_auth_query[] = "SELECT password FROM " MYSQLAUTH_USERS_TABLE_NAME @@ -106,10 +112,11 @@ static int db_flags = SQLITE_OPEN_READWRITE | typedef struct mysql_auth { - sqlite3 *handle; /**< SQLite3 database handle */ - char *cache_dir; /**< Custom cache directory location */ - bool inject_service_user; /**< Inject the service user into the list of users */ - bool skip_auth; /**< Authentication will always be successful */ + sqlite3 *handle; /**< SQLite3 database handle */ + char *cache_dir; /**< Custom cache directory location */ + bool inject_service_user; /**< Inject the service user into the list of users */ + bool skip_auth; /**< Authentication will always be successful */ + bool lower_case_table_names; /**< Disable database case-sensitivity */ } MYSQL_AUTH; /** Common structure for both backend and client authenticators */ From c4df28f64ac6df42a8b2b11ccaa0d716581b59e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 17 Jan 2018 09:56:50 +0200 Subject: [PATCH 2/3] MXS-1416: Skip directory creation with --config-check The log and data directories aren't created or checked when the --config-check option is given. --- server/core/config.c | 1 - server/core/gateway.cc | 60 ++++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index a15c20e4c..19132f2c8 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1624,7 +1624,6 @@ global_defaults() { uint8_t mac_addr[6] = ""; struct utsname uname_data; - gateway.config_check = false; gateway.n_threads = DEFAULT_NTHREADS; gateway.n_nbpoll = DEFAULT_NBPOLLS; gateway.pollsleep = DEFAULT_POLLSLEEP; diff --git a/server/core/gateway.cc b/server/core/gateway.cc index c9e006124..d7d726711 100644 --- a/server/core/gateway.cc +++ b/server/core/gateway.cc @@ -1338,8 +1338,6 @@ int main(int argc, char **argv) int rc = MAXSCALE_SHUTDOWN; int l; int i; - int n; - int ini_rval; intptr_t thread_id; int n_threads; /*< number of epoll listener threads */ int n_services; @@ -1353,24 +1351,24 @@ int main(int argc, char **argv) char* cnf_file_arg = NULL; /*< conf filename from cmd-line arg */ THREAD log_flush_thr; char* tmp_path; - char* tmp_var; int option_index; - int *syslog_enabled = &config_get_global_options()->syslog; /** Log to syslog */ - int *maxlog_enabled = &config_get_global_options()->maxlog; /** Log with MaxScale */ - int *log_to_shm = &config_get_global_options()->log_to_shm; /** Log to shared memory */ + MXS_CONFIG* cnf = config_get_global_options(); + ss_dassert(cnf); + int *syslog_enabled = &cnf->syslog; /** Log to syslog */ + int *maxlog_enabled = &cnf->maxlog; /** Log with MaxScale */ + int *log_to_shm = &cnf->log_to_shm; /** Log to shared memory */ ssize_t log_flush_timeout_ms = 0; sigset_t sigpipe_mask; sigset_t saved_mask; - bool config_check = false; bool to_stdout = false; void (*exitfunp[4])(void) = { mxs_log_finish, cleanup_process_datadir, write_footer, NULL }; - MXS_CONFIG* cnf = NULL; - int numlocks = 0; bool pid_file_created = false; + // NOTE: These are set here since global_defaults() is called inside config_load(). *syslog_enabled = 1; *maxlog_enabled = 1; *log_to_shm = 0; + cnf->config_check = false; maxscale_reset_starttime(); @@ -1657,7 +1655,7 @@ int main(int argc, char **argv) goto return_main; case 'c': - config_check = true; + cnf->config_check = true; break; default: @@ -1673,7 +1671,7 @@ int main(int argc, char **argv) } } - if (config_check) + if (cnf->config_check) { daemon_mode = false; to_stdout = true; @@ -1838,7 +1836,7 @@ int main(int argc, char **argv) { bool succp; - if (mkdir(get_logdir(), 0777) != 0 && errno != EEXIST) + if (!cnf->config_check && mkdir(get_logdir(), 0777) != 0 && errno != EEXIST) { fprintf(stderr, "Error: Cannot create log directory: %s\n", @@ -1885,20 +1883,23 @@ int main(int argc, char **argv) MXS_NOTICE("Commit: %s", MAXSCALE_COMMIT); #endif - /* - * Set the data directory. We use a unique directory name to avoid conflicts - * if multiple instances of MaxScale are being run on the same machine. - */ - if (create_datadir(get_datadir(), datadir)) + if (!cnf->config_check) { - set_process_datadir(datadir); - } - else - { - char errbuf[MXS_STRERROR_BUFLEN]; - MXS_ERROR("Cannot create data directory '%s': %d %s\n", - datadir, errno, strerror_r(errno, errbuf, sizeof(errbuf))); - goto return_main; + /* + * Set the data directory. We use a unique directory name to avoid conflicts + * if multiple instances of MaxScale are being run on the same machine. + */ + if (create_datadir(get_datadir(), datadir)) + { + set_process_datadir(datadir); + } + else + { + char errbuf[MXS_STRERROR_BUFLEN]; + MXS_ERROR("Cannot create data directory '%s': %d %s\n", + datadir, errno, strerror_r(errno, errbuf, sizeof(errbuf))); + goto return_main; + } } if (!daemon_mode) @@ -1940,9 +1941,6 @@ int main(int argc, char **argv) goto return_main; } - cnf = config_get_global_options(); - ss_dassert(cnf); - if (!qc_setup(cnf->qc_name, cnf->qc_args)) { const char* logerr = "Failed to initialise query classifier library."; @@ -1951,9 +1949,7 @@ int main(int argc, char **argv) goto return_main; } - cnf->config_check = config_check; - - if (!config_check) + if (!cnf->config_check) { /** Check if a MaxScale process is already running */ if (pid_file_exists()) @@ -2015,7 +2011,7 @@ int main(int argc, char **argv) goto return_main; } - if (config_check) + if (cnf->config_check) { MXS_NOTICE("Configuration was successfully verified."); rc = MAXSCALE_SHUTDOWN; From 40dfd1e0705b422ccac43de04248023281e4cce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Fri, 19 Jan 2018 11:23:28 +0200 Subject: [PATCH 3/3] MXS-1620: Fix RPM packaging scripts Unconditionally replace the `strip` binary with a dummy executable. --- BUILD/build_rpm_local.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/BUILD/build_rpm_local.sh b/BUILD/build_rpm_local.sh index 39006c778..d7edbc9c7 100755 --- a/BUILD/build_rpm_local.sh +++ b/BUILD/build_rpm_local.sh @@ -18,11 +18,11 @@ then ctest --output-on-failure || exit 1 fi -if [ $remove_strip == "yes" ] ; then - sudo rm -rf /usr/bin/strip - sudo touch /usr/bin/strip - sudo chmod a+x /usr/bin/strip -fi +# Never strip binaries +sudo rm -rf /usr/bin/strip +sudo touch /usr/bin/strip +sudo chmod a+x /usr/bin/strip + sudo make package res=$? if [ $res != 0 ] ; then