Attached is second version of the patch implementing the security model
below. The difference to previous version is that the optional argument
for stream_wrapper_register is now an integer flag, which allows to
extend it in the future.
Any comments/objections?
I think the problem could be solved this way:
1. By default, allow_remote_inclue=0, allow_remote_fopen=1
2. Stream can be of three types - remote, local and user/local.
3. User streams can be declared when registered as either remote or
user/local, remote being the default.
4. When operation on user/local stream is run, allow_remote_fopen is
disabled if allow_remote_include was disabled.
--
Stanislav Malyshev, Zend Products Engineer
[EMAIL PROTECTED] http://www.zend.com/
Index: main/streams/userspace.c
===================================================================
RCS file: /repository/php-src/main/streams/userspace.c,v
retrieving revision 1.42
diff -u -r1.42 userspace.c
--- main/streams/userspace.c 15 May 2007 13:01:47 -0000 1.42
+++ main/streams/userspace.c 11 Jun 2007 18:33:53 -0000
@@ -81,6 +81,7 @@
REGISTER_LONG_CONSTANT("STREAM_URL_STAT_QUIET",
PHP_STREAM_URL_STAT_QUIET, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("STREAM_MKDIR_RECURSIVE",
PHP_STREAM_MKDIR_RECURSIVE, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("STREAM_IS_LOCAL", PHP_STREAM_IS_LOCAL,
CONST_CS|CONST_PERSISTENT);
return SUCCESS;
}
@@ -215,6 +216,7 @@
int call_result;
php_stream *stream = NULL;
zval *zcontext = NULL;
+ char *old_allow_value = NULL;
/* Try to catch bad usage without preventing flexibility */
if (FG(user_stream_current_filename) != NULL && strcmp(filename,
FG(user_stream_current_filename)) == 0) {
@@ -223,6 +225,28 @@
}
FG(user_stream_current_filename) = filename;
+ /* if the user stream was registered as local and we are in include
context,
+ we add allow_url_include restrictions to allow_url_fopen ones */
+ /* we need only is_url == 0 here since if is_url == 1 and remote
wrappers
+ were restricted we wouldn't get here */
+ if(uwrap->wrapper.is_url == 0 &&
+ (options & STREAM_OPEN_FOR_INCLUDE) &&
+ (PG(allow_url_include_list) == NULL ||
strlen(PG(allow_url_include_list)) !=1 || PG(allow_url_include_list)[0] !=
'*')) {
+ /* we are in include and allow_url_include is restrictive */
+ char *include_value = zend_ini_string("allow_url_include",
sizeof("allow_url_include"), 0);
+ old_allow_value = zend_ini_string("allow_url_fopen",
sizeof("allow_url_fopen"), 0);
+ if(old_allow_value) {
+ old_allow_value = estrdup(old_allow_value);
+ }
+ if (zend_alter_ini_entry("allow_url_fopen",
sizeof("allow_url_fopen"),
+ include_value,
strlen(include_value),
+ PHP_INI_USER,
PHP_INI_STAGE_RUNTIME) == FAILURE) {
+ php_stream_wrapper_log_error(wrapper, options
TSRMLS_CC, "allow_url_fopen is more restrictive than allow_url_include");
+ efree(old_allow_value);
+ return NULL;
+ }
+ }
+
us = emalloc(sizeof(*us));
us->wrapper = uwrap;
@@ -258,6 +282,15 @@
FREE_ZVAL(us->object);
efree(us);
FG(user_stream_current_filename) = NULL;
+ if(old_allow_value) {
+ zend_alter_ini_entry("allow_url_fopen",
sizeof("allow_url_fopen"),
+ old_allow_value,
strlen(old_allow_value),
+ PHP_INI_USER, PHP_INI_STAGE_STARTUP);
+ /* hack here - using PHP_INI_STAGE_STARTUP
since we need the value back
+ but we can't just use restore_ini_entry since
it will restore
+ default value, not current value */
+ efree(old_allow_value);
+ }
return NULL;
} else {
if (retval_ptr) {
@@ -338,7 +371,16 @@
zval_ptr_dtor(&zfilename);
FG(user_stream_current_filename) = NULL;
-
+
+ if(old_allow_value) {
+ zend_alter_ini_entry("allow_url_fopen",
sizeof("allow_url_fopen"),
+
old_allow_value, strlen(old_allow_value),
+ PHP_INI_USER,
PHP_INI_STAGE_STARTUP);
+ /* hack here - using PHP_INI_STAGE_STARTUP since we need the
value back
+ but we can't just use restore_ini_entry since it will
restore
+ default value, not current value */
+ efree(old_allow_value);
+ }
return stream;
}
@@ -351,6 +393,7 @@
zval **args[2];
int call_result;
php_stream *stream = NULL;
+ char *old_allow_value = NULL;
/* Try to catch bad usage without preventing flexibility */
if (FG(user_stream_current_filename) != NULL && strcmp(filename,
FG(user_stream_current_filename)) == 0) {
@@ -359,6 +402,28 @@
}
FG(user_stream_current_filename) = filename;
+ /* if the user stream was registered as local and we are in include
context,
+ we add allow_url_include restrictions to allow_url_fopen ones */
+ /* we need only is_url == 0 here since if is_url == 1 and remote
wrappers
+ were restricted we wouldn't get here */
+ if(uwrap->wrapper.is_url == 0 &&
+ (options & STREAM_OPEN_FOR_INCLUDE) &&
+ (PG(allow_url_include_list) == NULL ||
strlen(PG(allow_url_include_list)) !=1 || PG(allow_url_include_list)[0] !=
'*')) {
+ /* we are in include and allow_url_include is restrictive */
+ char *include_value = zend_ini_string("allow_url_include",
sizeof("allow_url_include"), 0);
+ old_allow_value = zend_ini_string("allow_url_fopen",
sizeof("allow_url_fopen"), 0);
+ if(old_allow_value) {
+ old_allow_value = estrdup(old_allow_value);
+ }
+ if (zend_alter_ini_entry("allow_url_fopen",
sizeof("allow_url_fopen"),
+ include_value,
strlen(include_value),
+ PHP_INI_USER,
PHP_INI_STAGE_RUNTIME) == FAILURE) {
+ php_stream_wrapper_log_error(wrapper, options
TSRMLS_CC, "allow_url_fopen is more restrictive than allow_url_include");
+ efree(old_allow_value);
+ return NULL;
+ }
+ }
+
us = emalloc(sizeof(*us));
us->wrapper = uwrap;
@@ -424,11 +489,20 @@
FG(user_stream_current_filename) = NULL;
+ if(old_allow_value) {
+ zend_alter_ini_entry("allow_url_fopen",
sizeof("allow_url_fopen"),
+ old_allow_value, strlen(old_allow_value),
+ PHP_INI_USER, PHP_INI_STAGE_STARTUP);
+ /* hack here - using PHP_INI_STAGE_STARTUP since we need the
value back
+ but we can't just use restore_ini_entry since it will restore
+ default value, not current value */
+ efree(old_allow_value);
+ }
return stream;
}
-/* {{{ proto bool stream_wrapper_register(string protocol, string classname)
+/* {{{ proto bool stream_wrapper_register(string protocol, string classname[,
int flags])
Registers a custom URL protocol handler class */
PHP_FUNCTION(stream_wrapper_register)
{
@@ -436,8 +510,9 @@
int protocol_len, classname_len;
struct php_user_stream_wrapper * uwrap;
int rsrc_id;
+ long flags = 0;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &protocol,
&protocol_len, &classname, &classname_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &protocol,
&protocol_len, &classname, &classname_len, &flags) == FAILURE) {
RETURN_FALSE;
}
@@ -446,6 +521,7 @@
uwrap->classname = estrndup(classname, classname_len);
uwrap->wrapper.wops = &user_stream_wops;
uwrap->wrapper.abstract = uwrap;
+ uwrap->wrapper.is_url = ((flags & PHP_STREAM_IS_LOCAL) != 0);
rsrc_id = ZEND_REGISTER_RESOURCE(NULL, uwrap, le_protocols);
Index: ext/standard/basic_functions.c
===================================================================
RCS file: /repository/php-src/ext/standard/basic_functions.c,v
retrieving revision 1.859
diff -u -r1.859 basic_functions.c
--- ext/standard/basic_functions.c 9 Jun 2007 11:42:55 -0000 1.859
+++ ext/standard/basic_functions.c 11 Jun 2007 18:33:53 -0000
@@ -205,9 +205,10 @@
/* }}} */
/* {{{ main/streams/userspace.c */
static
-ZEND_BEGIN_ARG_INFO(arginfo_stream_wrapper_register, 0)
+ZEND_BEGIN_ARG_INFO_EX(arginfo_stream_wrapper_register, 0, 0, 2)
ZEND_ARG_INFO(0, protocol)
ZEND_ARG_INFO(0, classname)
+ ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO()
static
@@ -2347,6 +2348,11 @@
ZEND_END_ARG_INFO()
static
+ZEND_BEGIN_ARG_INFO(arginfo_stream_is_local, 0)
+ ZEND_ARG_INFO(0, stream)
+ZEND_END_ARG_INFO()
+
+static
ZEND_BEGIN_ARG_INFO_EX(arginfo_stream_select, 0, 0, 4)
ZEND_ARG_INFO(1, read_streams) /* ARRAY_INFO(1, read_streams, 1) */
ZEND_ARG_INFO(1, write_streams) /* ARRAY_INFO(1, write_streams, 1) */
@@ -3596,6 +3602,7 @@
PHP_FE(stream_wrapper_restore,
arginfo_stream_wrapper_restore)
PHP_FE(stream_get_wrappers,
arginfo_stream_get_wrappers)
PHP_FE(stream_get_transports,
arginfo_stream_get_transports)
+ PHP_FE(stream_is_local,
arginfo_stream_is_local)
PHP_FE(get_headers,
arginfo_get_headers)
#if HAVE_SYS_TIME_H || defined(PHP_WIN32)
Index: ext/standard/streamsfuncs.c
===================================================================
RCS file: /repository/php-src/ext/standard/streamsfuncs.c,v
retrieving revision 1.103
diff -u -r1.103 streamsfuncs.c
--- ext/standard/streamsfuncs.c 12 Apr 2007 13:15:17 -0000 1.103
+++ ext/standard/streamsfuncs.c 11 Jun 2007 18:33:53 -0000
@@ -1647,6 +1647,37 @@
}
/* }}} */
+/* {{{ proto bool stream_is_local(resource stream|string url) U
+*/
+PHP_FUNCTION(stream_is_local)
+{
+ zval *zstream;
+ php_stream *stream = NULL;
+ php_stream_wrapper *wrapper = NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zstream) ==
FAILURE) {
+ RETURN_FALSE;
+ }
+
+ if(Z_TYPE_P(zstream) == IS_RESOURCE) {
+ php_stream_from_zval(stream, &zstream);
+ if(stream == NULL) {
+ RETURN_FALSE;
+ }
+ wrapper = stream->wrapper;
+ } else {
+ convert_to_string_ex(&zstream);
+ wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(zstream),
NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC);
+ }
+
+ if(!wrapper) {
+ RETURN_FALSE;
+ }
+
+ RETURN_BOOL(wrapper->is_url==0);
+}
+/* }}} */
+
#ifdef HAVE_SHUTDOWN
/* {{{ proto int stream_socket_shutdown(resource stream, int how) U
causes all or part of a full-duplex connection on the socket associated
Index: ext/standard/streamsfuncs.h
===================================================================
RCS file: /repository/php-src/ext/standard/streamsfuncs.h,v
retrieving revision 1.19
diff -u -r1.19 streamsfuncs.h
--- ext/standard/streamsfuncs.h 1 Jan 2007 09:29:32 -0000 1.19
+++ ext/standard/streamsfuncs.h 11 Jun 2007 18:33:53 -0000
@@ -57,6 +57,7 @@
PHP_FUNCTION(stream_socket_shutdown);
PHP_FUNCTION(stream_socket_pair);
PHP_FUNCTION(stream_resolve_include_path);
+PHP_FUNCTION(stream_is_local);
/*
* Local variables:
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php