Merge branch '2.2' into 2.3

This commit is contained in:
Markus Mäkelä
2018-10-30 13:25:38 +02:00
9 changed files with 226 additions and 103 deletions

View File

@ -55,29 +55,33 @@
ON (u.user = t.user AND u.host = t.host) WHERE u.plugin IN ('', 'mysql_native_password') %s"
// Used with 10.2 or newer, supports composite roles
const char* mariadb_102_users_query
= // `t` is users that are not roles
"WITH RECURSIVE t AS ( "
" SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role, 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.is_role, 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)"
"), users AS ("
// Select the root row, the actual user
" SELECT t.user, t.host, t.db, t.select_priv, t.password, t.default_role AS role FROM t"
" WHERE t.is_role <> 'Y'"
" UNION"
// Recursively select all roles for the users
" SELECT u.user, u.host, t.db, t.select_priv, u.password, r.role FROM t"
" JOIN users AS u"
" ON (t.user = u.role)"
" LEFT JOIN mysql.roles_mapping AS r"
" ON (t.user = r.user)"
")"
"SELECT DISTINCT t.user, t.host, t.db, t.select_priv, t.password FROM users AS t %s";
const char* mariadb_102_users_query =
// `t` is users that are not roles
"WITH RECURSIVE t AS ( "
" SELECT u.user, u.host, d.db, u.select_priv, "
" IF(u.password <> '', u.password, u.authentication_string) AS password, "
" u.is_role, 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, "
" IF(u.password <> '', u.password, u.authentication_string), "
" u.is_role, 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)"
"), users AS ("
// Select the root row, the actual user
" SELECT t.user, t.host, t.db, t.select_priv, t.password, t.default_role AS role FROM t"
" WHERE t.is_role <> 'Y'"
" UNION"
// Recursively select all roles for the users
" SELECT u.user, u.host, t.db, t.select_priv, u.password, r.role FROM t"
" JOIN users AS u"
" ON (t.user = u.role)"
" LEFT JOIN mysql.roles_mapping AS r"
" ON (t.user = r.user)"
")"
"SELECT DISTINCT t.user, t.host, t.db, t.select_priv, t.password FROM users AS t %s";
// Query used with MariaDB 10.1, supports basic roles
const char* mariadb_users_query
@ -930,11 +934,12 @@ static bool roles_are_available(MYSQL* conn, SERVICE* service, SERVER* server)
return rval;
}
static void report_mdev13453_problem(MYSQL* con, SERVER* server)
static bool have_mdev13453_problem(MYSQL *con, SERVER *server)
{
if (server->version >= 100200 && server->version < 100211
&& mxs_pcre2_simple_match("SELECT command denied to user .* for table 'users'",
mysql_error(con), 0, NULL) == MXS_PCRE2_MATCH)
bool rval = false;
if (mxs_pcre2_simple_match("SELECT command denied to user .* for table 'users'",
mysql_error(con), 0, NULL) == MXS_PCRE2_MATCH)
{
char user[256] = "<failed to query user>"; // Enough for all user-hostname combinations
const char* quoted_user = "select concat(\"'\", user, \"'@'\", host, \"'\") as user "
@ -954,9 +959,51 @@ static void report_mdev13453_problem(MYSQL* con, SERVER* server)
mysql_free_result(res);
}
MXS_ERROR("Due to MDEV-13453, the service user requires extra grants on the `mysql` database. "
"To fix the problem, add the following grant: GRANT SELECT ON `mysql`.* TO %s", user);
MXS_WARNING("Due to MDEV-13453, the service user requires extra grants on the `mysql` database in "
"order for roles to be used. To fix the problem, add the following grant: "
"GRANT SELECT ON `mysql`.* TO %s", user);
rval = true;
}
return rval;
}
bool query_and_process_users(const char* query, MYSQL *con, sqlite3* handle, SERVICE* service, int* users)
{
bool rval = false;
if (mxs_mysql_query(con, "USE mysql") == 0 && // Set default database in case we use CTEs
mxs_mysql_query(con, query) == 0)
{
MYSQL_RES *result = mysql_store_result(con);
if (result)
{
MYSQL_ROW row;
while ((row = mysql_fetch_row(result)))
{
if (service->strip_db_esc)
{
strip_escape_chars(row[2]);
}
if (strchr(row[1], '/'))
{
merge_netmask(row[1]);
}
add_mysql_user(handle, row[0], row[1], row[2],
row[3] && strcmp(row[3], "Y") == 0, row[4]);
(*users)++;
}
mysql_free_result(result);
rval = true;
}
}
return rval;
}
int get_users_from_server(MYSQL* con, SERVER_REF* server_ref, SERVICE* service, SERV_LISTENER* listener)
@ -975,51 +1022,26 @@ int get_users_from_server(MYSQL* con, SERVER_REF* server_ref, SERVICE* service,
sqlite3* handle = get_handle(instance);
int users = 0;
if (query)
bool rv = query_and_process_users(query, con, handle, service, &users);
if (!rv && have_mdev13453_problem(con, server_ref->server))
{
if (mxs_mysql_query(con, "USE mysql") == 0 // Set default database in case we use CTEs
&& mxs_mysql_query(con, query) == 0)
{
MYSQL_RES* result = mysql_store_result(con);
if (result)
{
MYSQL_ROW row;
while ((row = mysql_fetch_row(result)))
{
if (service->strip_db_esc)
{
strip_escape_chars(row[2]);
}
if (strchr(row[1], '/'))
{
merge_netmask(row[1]);
}
add_mysql_user(handle,
row[0],
row[1],
row[2],
row[3] && strcmp(row[3], "Y") == 0,
row[4]);
users++;
}
mysql_free_result(result);
}
}
else
{
MXS_ERROR("Failed to load users from server '%s': %s", server_ref->server->name,
mysql_error(con));
report_mdev13453_problem(con, server_ref->server);
}
/**
* Try to work around MDEV-13453 by using a query without CTEs. Masquerading as
* a 10.1.10 server makes sure CTEs aren't used.
*/
MXS_FREE(query);
query = get_users_query(server_ref->server->version_string, 100110, service->enable_root, true);
rv = query_and_process_users(query, con, handle, service, &users);
}
if (!rv)
{
MXS_ERROR("Failed to load users from server '%s': %s", server_ref->server->name, mysql_error(con));
}
MXS_FREE(query);
/** Load the list of databases */
if (mxs_mysql_query(con, "SHOW DATABASES") == 0)
{

View File

@ -201,6 +201,29 @@ static char* gw_default_auth()
return (char*)"MySQLAuth";
}
std::string get_version_string(SERVICE* service)
{
std::string rval;
uint64_t intver = UINT64_MAX;
for (SERVER_REF* ref = service->dbref; ref; ref = ref->next)
{
if (ref->server->version && ref->server->version < intver)
{
rval = ref->server->version_string;
intver = ref->server->version;
}
}
// Get the version string from service if no server version is available
if (rval.empty())
{
rval = service->version_string;
}
return rval;
}
/**
* MySQLSendHandshake
*
@ -227,8 +250,6 @@ int MySQLSendHandshake(DCB* dcb)
uint8_t mysql_filler_ten[10] = {};
/* uint8_t mysql_last_byte = 0x00; not needed */
char server_scramble[GW_MYSQL_SCRAMBLE_SIZE + 1] = "";
char* version_string;
int len_version_string = 0;
bool is_maria = false;
@ -243,11 +264,9 @@ int MySQLSendHandshake(DCB* dcb)
}
}
MySQLProtocol* protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
GWBUF* buf;
version_string = dcb->service->version_string;
len_version_string = strlen(version_string);
MySQLProtocol *protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
GWBUF *buf;
std::string version = get_version_string(dcb->service);
gw_generate_random_str(server_scramble, GW_MYSQL_SCRAMBLE_SIZE);
@ -281,12 +300,11 @@ int MySQLSendHandshake(DCB* dcb)
int plugin_name_len = strlen(plugin_name);
mysql_payload_size =
sizeof(mysql_protocol_version) + (len_version_string + 1) + sizeof(mysql_thread_id_num) + 8
+ sizeof( /* mysql_filler */ uint8_t) + sizeof(mysql_server_capabilities_one)
+ sizeof(mysql_server_language)
+ sizeof(mysql_server_status) + sizeof(mysql_server_capabilities_two) + sizeof(mysql_scramble_len)
+ sizeof(mysql_filler_ten) + 12 + sizeof( /* mysql_last_byte */ uint8_t) + plugin_name_len
+ sizeof( /* mysql_last_byte */ uint8_t);
sizeof(mysql_protocol_version) + (version.length() + 1) + sizeof(mysql_thread_id_num) + 8 +
sizeof(/* mysql_filler */ uint8_t) + sizeof(mysql_server_capabilities_one) + sizeof(mysql_server_language) +
sizeof(mysql_server_status) + sizeof(mysql_server_capabilities_two) + sizeof(mysql_scramble_len) +
sizeof(mysql_filler_ten) + 12 + sizeof(/* mysql_last_byte */ uint8_t) + plugin_name_len +
sizeof(/* mysql_last_byte */ uint8_t);
// allocate memory for packet header + payload
if ((buf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size)) == NULL)
@ -311,8 +329,8 @@ int MySQLSendHandshake(DCB* dcb)
mysql_handshake_payload = mysql_handshake_payload + sizeof(mysql_protocol_version);
// write server version plus 0 filler
strcpy((char*)mysql_handshake_payload, version_string);
mysql_handshake_payload = mysql_handshake_payload + len_version_string;
strcpy((char *)mysql_handshake_payload, version.c_str());
mysql_handshake_payload = mysql_handshake_payload + version.length();
*mysql_handshake_payload = 0x00;

View File

@ -519,7 +519,7 @@ static int blr_file_create(ROUTER_INSTANCE* router, char* orig_file)
// Set final file name full path
strcat(path, file);
int fd = open(path, O_RDWR | O_CREAT, 0666);
int fd = open(path, O_RDWR | O_CREAT, 0660);
if (fd != -1)
{
@ -621,7 +621,7 @@ void blr_file_append(ROUTER_INSTANCE* router, char* file)
// Add filename
strcat(path, file);
if ((fd = open(path, flags, 0666)) == -1)
if ((fd = open(path, flags, 0660)) == -1)
{
MXS_ERROR("Failed to open binlog file %s for append.",
path);
@ -940,7 +940,7 @@ BLFILE* blr_open_binlog(ROUTER_INSTANCE* router,
/* Add file name */
strcat(path, binlog);
if ((file->fd = open(path, O_RDONLY, 0666)) == -1)
if ((file->fd = open(path, O_RDONLY, 0660)) == -1)
{
MXS_ERROR("Failed to open binlog file %s", path);
MXS_FREE(file);
@ -1531,7 +1531,7 @@ void blr_cache_response(ROUTER_INSTANCE* router, char* response, GWBUF* buf)
strcat(path, "/");
strcat(path, response);
if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1)
if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0660)) == -1)
{
return;
}

View File

@ -160,7 +160,7 @@ int main(int argc, char** argv)
exit(EXIT_FAILURE);
}
int fd = open(path, binlog_file.fix ? O_RDWR : O_RDONLY, 0666);
int fd = open(path, binlog_file.fix ? O_RDWR : O_RDONLY, 0660);
if (fd == -1)
{
printf("ERROR: Failed to open binlog file %s: %s.\n",