From 02e7d5654dd83600f3c1e27b7c61ab1f4ea59138 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 5 Feb 2019 07:19:36 +0100 Subject: [PATCH] Crude "auto-load-data-local-infile" mode Disable LOAD DATA LOCAL INFILE suport by default and auto-enable it for the duration of one query, if the query string starts with the word "load". In all other cases the application should enable LOAD DATA LOCAL INFILE support explicitly. (Based on Sergei Golubchiks 5.5 server patch) --- CMakeLists.txt | 10 ++++++++++ include/ma_common.h | 8 ++++++++ include/ma_config.h.in | 5 +++++ libmariadb/CMakeLists.txt | 1 - libmariadb/ma_loaddata.c | 7 +++++-- libmariadb/mariadb_lib.c | 21 +++++++++++++++++++-- 6 files changed, 47 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c4eee49..853089ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -334,6 +334,16 @@ IF(NOT WITH_SSL STREQUAL "OFF") MARK_AS_ADVANCED(SSL_SOURCES) ENDIF() +SET(ENABLED_LOCAL_INFILE "AUTO" CACHE STRING "If we should should enable LOAD DATA LOCAL by default (OFF/ON/AUTO)") +MARK_AS_ADVANCED(ENABLED_LOCAL_INFILE) +IF (ENABLED_LOCAL_INFILE MATCHES "^(0|FALSE)$") + SET(ENABLED_LOCAL_INFILE OFF) +ELSEIF(ENABLED_LOCAL_INFILE MATCHES "^(1|TRUE)$") + SET(ENABLED_LOCAL_INFILE ON) +ELSEIF (NOT ENABLED_LOCAL_INFILE MATCHES "^(ON|OFF|AUTO)$") + MESSAGE(FATAL_ERROR "ENABLED_LOCAL_INFILE must be one of OFF, ON, AUTO") +ENDIF() + IF(NOT WIN32) INCLUDE(${CC_SOURCE_DIR}/cmake/FindIconv.cmake) ENDIF() diff --git a/include/ma_common.h b/include/ma_common.h index 01f7ba16..8246eb00 100644 --- a/include/ma_common.h +++ b/include/ma_common.h @@ -31,6 +31,13 @@ enum enum_multi_status { COM_MULTI_END }; + +typedef enum { + ALWAYS_ACCEPT, /* heuristics is disabled, use CLIENT_LOCAL_FILES */ + WAIT_FOR_QUERY, /* heuristics is enabled, not sending files */ + ACCEPT_FILE_REQUEST /* heuristics is enabled, ready to send a file */ +} auto_local_infile_state; + typedef struct st_mariadb_db_driver { struct st_mariadb_client_plugin_DB *plugin; @@ -71,6 +78,7 @@ struct st_mysql_options_extension { char *server_public_key; char *proxy_header; size_t proxy_header_len; + my_bool auto_local_infile; }; typedef struct st_connection_handler diff --git a/include/ma_config.h.in b/include/ma_config.h.in index ae5ab0d8..8f12ee67 100644 --- a/include/ma_config.h.in +++ b/include/ma_config.h.in @@ -131,5 +131,10 @@ #cmakedefine SOCKET_SIZE_TYPE @SOCKET_SIZE_TYPE@ +#define LOCAL_INFILE_MODE_OFF 0 +#define LOCAL_INFILE_MODE_ON 1 +#define LOCAL_INFILE_MODE_AUTO 2 +#define ENABLED_LOCAL_INFILE LOCAL_INFILE_MODE_@ENABLED_LOCAL_INFILE@ + #define MARIADB_DEFAULT_CHARSET "@DEFAULT_CHARSET@" diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index 874f9f5a..a1f039e0 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -1,7 +1,6 @@ INCLUDE_DIRECTORIES(${CC_SOURCE_DIR}/include ${CC_SOURCE_DIR}/libmariadb) -ADD_DEFINITIONS(-D ENABLED_LOCAL_INFILE) ADD_DEFINITIONS(-D HAVE_COMPRESS) ADD_DEFINITIONS(-D LIBMARIADB) ADD_DEFINITIONS(-D THREAD) diff --git a/libmariadb/ma_loaddata.c b/libmariadb/ma_loaddata.c index 9332c7cf..c59285ec 100644 --- a/libmariadb/ma_loaddata.c +++ b/libmariadb/ma_loaddata.c @@ -50,6 +50,7 @@ #ifdef _WIN32 #include #endif +#include typedef struct st_mysql_infile_info { @@ -183,7 +184,7 @@ void STDCALL mysql_set_local_infile_handler(MYSQL *conn, /* }}} */ /* {{{ mysql_handle_local_infile */ -my_bool mysql_handle_local_infile(MYSQL *conn, const char *filename) +my_bool mysql_handle_local_infile(MYSQL *conn, const char *filename, my_bool can_local_infile) { unsigned int buflen= 4096; int bufread; @@ -199,7 +200,9 @@ my_bool mysql_handle_local_infile(MYSQL *conn, const char *filename) mysql_set_local_infile_default(conn); } - if (!(conn->options.client_flag & CLIENT_LOCAL_FILES)) { + if (!(conn->options.client_flag & CLIENT_LOCAL_FILES) || + !can_local_infile) + { my_set_error(conn, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, "Load data local infile forbidden"); /* write empty packet to server */ ma_net_write(&conn->net, (unsigned char *)"", 0); diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 62470253..f587ff1e 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -87,7 +87,7 @@ extern void release_configuration_dirs(); extern char **get_default_configuration_dirs(); extern my_bool ma_init_done; extern my_bool mysql_ps_subsystem_initialized; -extern my_bool mysql_handle_local_infile(MYSQL *mysql, const char *filename); +extern my_bool mysql_handle_local_infile(MYSQL *mysql, const char *filename, my_bool can_local_infile); extern const MARIADB_CHARSET_INFO * mysql_find_charset_nr(uint charsetnr); extern const MARIADB_CHARSET_INFO * mysql_find_charset_name(const char * const name); extern my_bool set_default_charset_by_name(const char *cs_name, myf flags __attribute__((unused))); @@ -428,6 +428,14 @@ int ma_simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, size_t length, my_bool skipp_check, void *opt_arg) { + if ((mysql->options.client_flag & CLIENT_LOCAL_FILES) && + mysql->options.extension && mysql->options.extension->auto_local_infile == WAIT_FOR_QUERY && + arg && (*arg == 'l' || *arg == 'L') && + command == COM_QUERY) + { + if (strncasecmp(arg, "load", 4) == 0) + mysql->options.extension->auto_local_infile= ACCEPT_FILE_REQUEST; + } return mysql->methods->db_command(mysql, command, arg, length, skipp_check, opt_arg); } @@ -1009,6 +1017,8 @@ mysql_init(MYSQL *mysql) */ #ifdef ENABLED_LOCAL_INFILE mysql->options.client_flag|= CLIENT_LOCAL_FILES; + OPT_SET_EXTENDED_VALUE_INT(&mysql->options, auto_local_infile, ENABLED_LOCAL_INFILE == LOCAL_INFILE_MODE_AUTO + ? WAIT_FOR_QUERY : ALWAYS_ACCEPT); #endif mysql->options.reconnect= 0; return mysql; @@ -2113,6 +2123,10 @@ int mthd_my_read_query_result(MYSQL *mysql) ulong field_count; MYSQL_DATA *fields; ulong length; + my_bool can_local_infile= (mysql->options.extension) && (mysql->options.extension->auto_local_infile != WAIT_FOR_QUERY); + + if (mysql->options.extension && mysql->options.extension->auto_local_infile == ACCEPT_FILE_REQUEST) + mysql->options.extension->auto_local_infile= WAIT_FOR_QUERY; if (!mysql || (length = ma_net_safe_read(mysql)) == packet_error) { @@ -2125,7 +2139,7 @@ get_info: return ma_read_ok_packet(mysql, pos, length); if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */ { - int error=mysql_handle_local_infile(mysql, (char *)pos); + int error=mysql_handle_local_infile(mysql, (char *)pos, can_local_infile); if ((length=ma_net_safe_read(mysql)) == packet_error || error) return(-1); @@ -2669,6 +2683,9 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) mysql->options.client_flag|= CLIENT_LOCAL_FILES; else mysql->options.client_flag&= ~CLIENT_LOCAL_FILES; + if (arg1) + OPT_SET_EXTENDED_VALUE_INT(&mysql->options, auto_local_infile, *(uint*)arg1 == LOCAL_INFILE_MODE_AUTO + ? WAIT_FOR_QUERY : ALWAYS_ACCEPT); break; case MYSQL_INIT_COMMAND: options_add_initcommand(&mysql->options, (char *)arg1);