summaryrefslogtreecommitdiff
path: root/ext/mysqlnd/mysqlnd_loaddata.c
diff options
context:
space:
mode:
authorDarek Slusarczyk <dariusz.slusarczyk@oracle.com>2021-02-22 11:03:24 +0100
committerNikita Popov <nikita.ppv@gmail.com>2021-02-23 09:30:46 +0100
commitda011a312a6c6cd7ff12fe1aa0de1e33fba2f167 (patch)
treec3e37dadfa5ea7aec39ca3695779fe30c7bd96f6 /ext/mysqlnd/mysqlnd_loaddata.c
parent7f8ea83ef438fbcfa1cbc636d701491d4e773245 (diff)
downloadphp-git-da011a312a6c6cd7ff12fe1aa0de1e33fba2f167.tar.gz
Fix #80329: Add option to specify LOAD DATA LOCAL white list folder
* allow the user to specify a folder where files that can be sent via LOAD DATA LOCAL can exist * add mysqli.local_infile_directory for mysqli (ignored if mysqli.allow_local_infile is enabled) * add PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY for pdo_mysql (ignored if PDO::MYSQL_ATTR_LOCAL_INFILE is enabled) * add related tests * fixes for building with libmysql 8.x * small improvement in existing tests * update php.ini-[development|production] files Closes GH-6448. Co-authored-by: Nikita Popov <nikic@php.net>
Diffstat (limited to 'ext/mysqlnd/mysqlnd_loaddata.c')
-rw-r--r--ext/mysqlnd/mysqlnd_loaddata.c45
1 files changed, 42 insertions, 3 deletions
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;