Skip to content

Commit d1cc4b5

Browse files
committed
Resolve open_basedir paths on ini update
1 parent fced34e commit d1cc4b5

File tree

7 files changed

+61
-45
lines changed

7 files changed

+61
-45
lines changed

Zend/tests/gh10469.phpt

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ $originalDir = __DIR__;
88
$tmpDir = $originalDir . '/gh10469_tmp';
99
@mkdir($tmpDir, 0777, true);
1010
chdir($tmpDir);
11-
ini_set('open_basedir', ini_get('open_basedir') . ':./..');
12-
ini_set('open_basedir', ini_get('open_basedir') . ':./../');
13-
ini_set('open_basedir', ini_get('open_basedir') . ':/a/');
11+
ini_set('open_basedir', ini_get('open_basedir') . ':.' . DIRECTORY_SEPARATOR . '..');
12+
ini_set('open_basedir', ini_get('open_basedir') . ':.' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR);
13+
ini_set('open_basedir', ini_get('open_basedir') . ':' . DIRECTORY_SEPARATOR . 'a' . DIRECTORY_SEPARATOR);
1414

1515
chdir($originalDir);
1616
var_dump(ini_get('open_basedir'));
@@ -20,4 +20,4 @@ var_dump(ini_get('open_basedir'));
2020
@rmdir(__DIR__ . '/gh10469_tmp');
2121
?>
2222
--EXPECTF--
23-
string(%d) "%stests"
23+
string(%d) "%stests:.%e..:.%e..%e"

Zend/zend.c

+23
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
#include "zend_call_stack.h"
3939
#include "zend_max_execution_timer.h"
4040
#include "Optimizer/zend_optimizer.h"
41+
#include "php.h"
42+
#include "php_globals.h"
43+
44+
// FIXME: Breaks the declaration of the function below
45+
#undef zenderror
4146

4247
static size_t global_map_ptr_last = 0;
4348
static bool startup_done = false;
@@ -1259,11 +1264,29 @@ void zend_call_destructors(void) /* {{{ */
12591264
}
12601265
/* }}} */
12611266

1267+
static void zend_release_open_basedir(void)
1268+
{
1269+
/* Release custom open_basedir config, this needs to happen before ini shutdown */
1270+
if (PG(open_basedir)) {
1271+
zend_ini_entry *ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "open_basedir", strlen("open_basedir"));
1272+
/* ini_entry->modified is unreliable, it might also be set when on_update has failed. */
1273+
if (ini_entry
1274+
&& ini_entry->modified
1275+
&& ini_entry->value != ini_entry->orig_value) {
1276+
efree(PG(open_basedir));
1277+
PG(open_basedir) = NULL;
1278+
}
1279+
}
1280+
}
1281+
12621282
ZEND_API void zend_deactivate(void) /* {{{ */
12631283
{
12641284
/* we're no longer executing anything */
12651285
EG(current_execute_data) = NULL;
12661286

1287+
/* Needs to run before zend_ini_deactivate(). */
1288+
zend_release_open_basedir();
1289+
12671290
zend_try {
12681291
shutdown_scanner();
12691292
} zend_end_try();

ext/session/tests/session_save_path_variation5.phpt

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ if (file_exists($sessions) === TRUE) {
2828

2929
var_dump(mkdir($sessions));
3030
var_dump(chdir($sessions));
31+
ini_set('open_basedir', '.');
3132
ini_set("session.save_path", $directory);
3233
var_dump(session_save_path());
3334

@@ -45,6 +46,6 @@ rmdir($sessions);
4546
bool(true)
4647
bool(true)
4748

48-
Warning: ini_set(): open_basedir restriction in effect. File(%s) is not within the allowed path(s): (.) in %s on line %d
49+
Warning: ini_set(): open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%ssession_save_path_variation5) in %s on line %d
4950
string(0) ""
5051
Done

main/fopen_wrappers.c

+22-31
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "ext/standard/php_standard.h"
3939
#include "zend_compile.h"
4040
#include "php_network.h"
41+
#include "zend_smart_str.h"
4142

4243
#if HAVE_PWD_H
4344
#include <pwd.h>
@@ -81,60 +82,50 @@ PHPAPI ZEND_INI_MH(OnUpdateBaseDir)
8182
return SUCCESS;
8283
}
8384

84-
/* Otherwise we're in runtime */
85-
if (!*p || !**p) {
86-
/* open_basedir not set yet, go ahead and give it a value */
87-
*p = ZSTR_VAL(new_value);
88-
return SUCCESS;
89-
}
90-
9185
/* Shortcut: When we have a open_basedir and someone tries to unset, we know it'll fail */
9286
if (!new_value || !*ZSTR_VAL(new_value)) {
9387
return FAILURE;
9488
}
9589

9690
/* Is the proposed open_basedir at least as restrictive as the current setting? */
91+
smart_str buf = {0};
9792
ptr = pathbuf = estrdup(ZSTR_VAL(new_value));
9893
while (ptr && *ptr) {
9994
end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
10095
if (end != NULL) {
10196
*end = '\0';
10297
end++;
10398
}
104-
/* Don't allow paths with a parent dir component (..) to be set at runtime */
105-
char *substr_pos = ptr;
106-
while (*substr_pos) {
107-
// Check if we have a .. path component
108-
if (substr_pos[0] == '.'
109-
&& substr_pos[1] == '.'
110-
&& (substr_pos[2] == '\0' || IS_SLASH(substr_pos[2]))) {
111-
efree(pathbuf);
112-
return FAILURE;
113-
}
114-
// Skip to the next path component
115-
while (true) {
116-
substr_pos++;
117-
if (*substr_pos == '\0' || *substr_pos == DEFAULT_DIR_SEPARATOR) {
118-
goto no_parent_dir_component;
119-
} else if (IS_SLASH(*substr_pos)) {
120-
// Also skip the slash
121-
substr_pos++;
122-
break;
123-
}
124-
}
99+
char resolved_name[MAXPATHLEN + 1];
100+
if (expand_filepath(ptr, resolved_name) == NULL) {
101+
smart_str_free(&buf);
102+
return FAILURE;
125103
}
126-
no_parent_dir_component:
127-
if (php_check_open_basedir_ex(ptr, 0) != 0) {
104+
if (php_check_open_basedir_ex(resolved_name, 0) != 0) {
128105
/* At least one portion of this open_basedir is less restrictive than the prior one, FAIL */
129106
efree(pathbuf);
107+
smart_str_free(&buf);
130108
return FAILURE;
131109
}
110+
if (smart_str_get_len(&buf) != 0) {
111+
smart_str_appendc(&buf, DEFAULT_DIR_SEPARATOR);
112+
}
113+
smart_str_appends(&buf, resolved_name);
132114
ptr = end;
133115
}
134116
efree(pathbuf);
135117

136118
/* Everything checks out, set it */
137-
*p = ZSTR_VAL(new_value);
119+
if (*p) {
120+
/* Unfortunately entry->modified has already been set to true so we compare entry->value
121+
* against entry->original_value. */
122+
if (entry->value != entry->orig_value) {
123+
efree(*p);
124+
}
125+
}
126+
zend_string *tmp = smart_str_extract(&buf);
127+
*p = estrdup(ZSTR_VAL(tmp));
128+
zend_string_release(tmp);
138129

139130
return SUCCESS;
140131
}

main/php.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ ssize_t pread(int, void *, size_t, off64_t);
296296
#endif
297297

298298
BEGIN_EXTERN_C()
299-
void phperror(char *error);
299+
void phperror(const char *error);
300300
PHPAPI size_t php_write(void *buf, size_t size);
301301
PHPAPI size_t php_printf(const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 1, 2);
302302
PHPAPI size_t php_printf_unchecked(const char *format, ...);

tests/security/bug76359.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ chdir("..");
1010
chdir("..");
1111
?>
1212
--EXPECTF--
13-
bool(false)
13+
string(%d) "%ssecurity"
1414

1515
Warning: chdir(): open_basedir restriction in effect. File(..) is not within the allowed path(s): (%s) in %s on line %d
1616
--CLEAN--

tests/security/open_basedir_readlink.phpt

+8-7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ $symlink = ($initdir."/test/ok/symlink.txt");
2121
var_dump(symlink($target, $symlink));
2222

2323
chdir($initdir."/test/ok");
24+
ini_set("open_basedir", ".");
2425

2526
var_dump(readlink("symlink.txt"));
2627
var_dump(readlink("../ok/symlink.txt"));
@@ -49,24 +50,24 @@ bool(true)
4950
bool(true)
5051
bool(true)
5152

52-
Warning: readlink(): open_basedir restriction in effect. File(symlink.txt) is not within the allowed path(s): (.) in %s on line %d
53+
Warning: readlink(): open_basedir restriction in effect. File(symlink.txt) is not within the allowed path(s): (%sok) in %s on line %d
5354
bool(false)
5455

55-
Warning: readlink(): open_basedir restriction in effect. File(../ok/symlink.txt) is not within the allowed path(s): (.) in %s on line %d
56+
Warning: readlink(): open_basedir restriction in effect. File(../ok/symlink.txt) is not within the allowed path(s): (%sok) in %s on line %d
5657
bool(false)
5758

58-
Warning: readlink(): open_basedir restriction in effect. File(../ok/./symlink.txt) is not within the allowed path(s): (.) in %s on line %d
59+
Warning: readlink(): open_basedir restriction in effect. File(../ok/./symlink.txt) is not within the allowed path(s): (%sok) in %s on line %d
5960
bool(false)
6061

61-
Warning: readlink(): open_basedir restriction in effect. File(./symlink.txt) is not within the allowed path(s): (.) in %s on line %d
62+
Warning: readlink(): open_basedir restriction in effect. File(./symlink.txt) is not within the allowed path(s): (%sok) in %s on line %d
6263
bool(false)
6364

64-
Warning: readlink(): open_basedir restriction in effect. File(%s/test/ok/symlink.txt) is not within the allowed path(s): (.) in %s on line %d
65+
Warning: readlink(): open_basedir restriction in effect. File(%s/test/ok/symlink.txt) is not within the allowed path(s): (%sok) in %s on line %d
6566
bool(false)
6667

67-
Warning: symlink(): open_basedir restriction in effect. File(%s/test/bad/bad.txt) is not within the allowed path(s): (.) in %s on line %d
68+
Warning: symlink(): open_basedir restriction in effect. File(%s/test/bad/bad.txt) is not within the allowed path(s): (%sok) in %s on line %d
6869
bool(false)
6970

70-
Warning: readlink(): open_basedir restriction in effect. File(%s/test/ok/symlink.txt) is not within the allowed path(s): (.) in %s on line %d
71+
Warning: readlink(): open_basedir restriction in effect. File(%s/test/ok/symlink.txt) is not within the allowed path(s): (%sok) in %s on line %d
7172
bool(false)
7273
*** Finished testing open_basedir configuration [readlink] ***

0 commit comments

Comments
 (0)