summaryrefslogtreecommitdiff
path: root/ext/mysqlnd
diff options
context:
space:
mode:
Diffstat (limited to 'ext/mysqlnd')
-rw-r--r--ext/mysqlnd/mysqlnd_connection.c17
-rw-r--r--ext/mysqlnd/mysqlnd_enum_n_def.h2
-rw-r--r--ext/mysqlnd/mysqlnd_loaddata.c45
-rw-r--r--ext/mysqlnd/mysqlnd_structs.h2
4 files changed, 63 insertions, 3 deletions
diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c
index 168e161d1b..8730de04b1 100644
--- a/ext/mysqlnd/mysqlnd_connection.c
+++ b/ext/mysqlnd/mysqlnd_connection.c
@@ -251,6 +251,10 @@ MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn)
mnd_pefree(conn->options->connect_attr, pers);
conn->options->connect_attr = NULL;
}
+ if (conn->options->local_infile_directory) {
+ mnd_pefree(conn->options->local_infile_directory, pers);
+ conn->options->local_infile_directory = NULL;
+ }
}
/* }}} */
@@ -1648,6 +1652,19 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c
conn->options->flags &= ~CLIENT_LOCAL_FILES;
}
break;
+ case MYSQL_OPT_LOAD_DATA_LOCAL_DIR:
+ {
+ if (conn->options->local_infile_directory) {
+ mnd_pefree(conn->options->local_infile_directory, conn->persistent);
+ }
+
+ if (!value || (*value == '\0')) {
+ conn->options->local_infile_directory = NULL;
+ } else {
+ conn->options->local_infile_directory = mnd_pestrdup(value, conn->persistent);
+ }
+ break;
+ }
case MYSQL_INIT_COMMAND:
{
char ** new_init_commands;
diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h
index b65e8523b2..80be26e622 100644
--- a/ext/mysqlnd/mysqlnd_enum_n_def.h
+++ b/ext/mysqlnd/mysqlnd_enum_n_def.h
@@ -129,6 +129,7 @@
#define CR_PARAMS_NOT_BOUND 2031
#define CR_INVALID_PARAMETER_NO 2034
#define CR_INVALID_BUFFER_USE 2035
+#define CR_LOAD_DATA_LOCAL_INFILE_REJECTED 2068
#define MYSQLND_EE_FILENOTFOUND 7890
@@ -247,6 +248,7 @@ typedef enum mysqlnd_client_option
MYSQL_OPT_NET_BUFFER_LENGTH,
MYSQL_OPT_TLS_VERSION,
MYSQL_OPT_SSL_MODE,
+ MYSQL_OPT_LOAD_DATA_LOCAL_DIR,
MYSQLND_DEPRECATED_ENUM1 = 200,
MYSQLND_OPT_INT_AND_FLOAT_NATIVE = 201,
MYSQLND_OPT_NET_CMD_BUFFER_SIZE = 202,
diff --git a/ext/mysqlnd/mysqlnd_loaddata.c b/ext/mysqlnd/mysqlnd_loaddata.c
index 4cd0433877..c00800c451 100644
--- a/ext/mysqlnd/mysqlnd_loaddata.c
+++ b/ext/mysqlnd/mysqlnd_loaddata.c
@@ -149,12 +149,51 @@ mysqlnd_handle_local_infile(MYSQLND_CONN_DATA * conn, const char * const filenam
MYSQLND_INFILE infile;
MYSQLND_PFC * net = conn->protocol_frame_codec;
MYSQLND_VIO * vio = conn->vio;
+ bool is_local_infile_enabled = (conn->options->flags & CLIENT_LOCAL_FILES) == CLIENT_LOCAL_FILES;
+ const char* local_infile_directory = conn->options->local_infile_directory;
+ bool is_local_infile_dir_set = local_infile_directory != NULL;
+ bool prerequisities_ok = TRUE;
DBG_ENTER("mysqlnd_handle_local_infile");
- if (!(conn->options->flags & CLIENT_LOCAL_FILES)) {
- SET_CLIENT_ERROR(conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE,
- "LOAD DATA LOCAL INFILE is forbidden, check mysqli.allow_local_infile");
+ /*
+ if local_infile is disabled, and local_infile_dir is not set, then operation is forbidden
+ */
+ if (!is_local_infile_enabled && !is_local_infile_dir_set) {
+ SET_CLIENT_ERROR(conn->error_info, CR_LOAD_DATA_LOCAL_INFILE_REJECTED, UNKNOWN_SQLSTATE,
+ "LOAD DATA LOCAL INFILE is forbidden, check related settings like "
+ "mysqli.allow_local_infile|mysqli.local_infile_directory or "
+ "PDO::MYSQL_ATTR_LOCAL_INFILE|PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY");
+ prerequisities_ok = FALSE;
+ }
+
+ /*
+ if local_infile_dir is set, then check whether it actually exists, and is accessible
+ */
+ if (is_local_infile_dir_set) {
+ php_stream *stream = php_stream_opendir(local_infile_directory, REPORT_ERRORS, NULL);
+ if (stream) {
+ php_stream_closedir(stream);
+ } else {
+ SET_CLIENT_ERROR(conn->error_info, CR_LOAD_DATA_LOCAL_INFILE_REJECTED, UNKNOWN_SQLSTATE, "cannot open local_infile_directory");
+ prerequisities_ok = FALSE;
+ }
+ }
+
+ /*
+ if local_infile is disabled and local_infile_dir is set, then we have to check whether
+ filename is located inside its subtree
+ but only in such a case, because when local_infile is enabled, then local_infile_dir is ignored
+ */
+ if (prerequisities_ok && !is_local_infile_enabled && is_local_infile_dir_set) {
+ if (php_check_specific_open_basedir(local_infile_directory, filename) == -1) {
+ SET_CLIENT_ERROR(conn->error_info, CR_LOAD_DATA_LOCAL_INFILE_REJECTED, UNKNOWN_SQLSTATE,
+ "LOAD DATA LOCAL INFILE DIRECTORY restriction in effect. Unable to open file");
+ prerequisities_ok = FALSE;
+ }
+ }
+
+ if (!prerequisities_ok) {
/* write empty packet to server */
ret = net->data->m.send(net, vio, empty_packet, 0, conn->stats, conn->error_info);
*is_warning = TRUE;
diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h
index 75d8af9acd..6ee057fc72 100644
--- a/ext/mysqlnd/mysqlnd_structs.h
+++ b/ext/mysqlnd/mysqlnd_structs.h
@@ -231,6 +231,8 @@ typedef struct st_mysqlnd_session_options
unsigned int max_allowed_packet;
bool int_and_float_native;
+
+ char *local_infile_directory;
} MYSQLND_SESSION_OPTIONS;