According to the plan below, attached is the patch that restricts user streams from executing dangerous operations inside include context. Please comment.

I think the problem could be solved this way:
0. allow_url_include and allow_url_fopen renamed to
allow_remote_include and allow_remote_fopen (not really necessary,
just much cleaner, if you don't like it, ignore it for now).
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: ext/standard/basic_functions.c
===================================================================
RCS file: /repository/php-src/ext/standard/basic_functions.c,v
retrieving revision 1.858
diff -u -r1.858 basic_functions.c
--- ext/standard/basic_functions.c      22 May 2007 14:32:39 -0000      1.858
+++ ext/standard/basic_functions.c      30 May 2007 00:14:07 -0000
@@ -2343,6 +2343,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) */
@@ -3591,6 +3596,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 30 May 2007 00:14:10 -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 30 May 2007 00:14:10 -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:
Index: main/internal_functions_win32.c
===================================================================
RCS file: /repository/php-src/main/internal_functions_win32.c,v
retrieving revision 1.90
diff -u -r1.90 internal_functions_win32.c
--- main/internal_functions_win32.c     1 Jan 2007 09:29:35 -0000       1.90
+++ main/internal_functions_win32.c     30 May 2007 00:14:10 -0000
@@ -98,12 +98,18 @@
 #include "ext/sqlite/php_sqlite.h"
 #endif
 #include "ext/com_dotnet/php_com_dotnet.h"
+#include "ext/spl/php_spl.h"
+#include "ext/date/php_date.h"
+#include "ext/unicode/php_unicode.h"
 /* }}} */
 
 /* {{{ php_builtin_extensions[]
  */
 static zend_module_entry *php_builtin_extensions[] = {
        phpext_standard_ptr
+       ,phpext_spl_ptr
+       ,phpext_date_ptr
+       ,phpext_unicode_ptr
 #if HAVE_BCMATH
        ,phpext_bcmath_ptr
 #endif
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    30 May 2007 00:14:10 -0000
@@ -215,6 +215,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 +224,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 +281,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 +370,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 +392,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 +401,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 +488,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[, 
bool remote])
    Registers a custom URL protocol handler class */
 PHP_FUNCTION(stream_wrapper_register)
 {
@@ -436,8 +509,9 @@
        int protocol_len, classname_len;
        struct php_user_stream_wrapper * uwrap;
        int rsrc_id;
+       zend_bool remote = 1;
        
-       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|b", &protocol, 
&protocol_len, &classname, &classname_len, &remote) == FAILURE) {
                RETURN_FALSE;
        }
        
@@ -446,6 +520,7 @@
        uwrap->classname = estrndup(classname, classname_len);
        uwrap->wrapper.wops = &user_stream_wops;
        uwrap->wrapper.abstract = uwrap;
+       uwrap->wrapper.is_url = (remote != 0);
 
        rsrc_id = ZEND_REGISTER_RESOURCE(NULL, uwrap, le_protocols);
-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to