diff options
Diffstat (limited to 'ext/mysqlnd')
-rw-r--r-- | ext/mysqlnd/mysqlnd_connection.c | 17 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd_enum_n_def.h | 2 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd_loaddata.c | 45 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd_structs.h | 2 |
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; |