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);