]> granicus.if.org Git - php/commitdiff
Added ability to create local or remote (URL) user streams
authorDmitry Stogov <dmitry@php.net>
Tue, 3 Jul 2007 10:22:55 +0000 (10:22 +0000)
committerDmitry Stogov <dmitry@php.net>
Tue, 3 Jul 2007 10:22:55 +0000 (10:22 +0000)
Local user streams must not be able to open(), URLs if allow_url_include is off
Implemented new function stream_is_local()
[
- stream_wrapper_register() extended with additional optional argument "flags"
  of type long. This time only one flag is implemented - STREAM_IS_URL, that
  means that userstream wrapper is remote (URL). By default stream is local.
- stream_is_local() is a new function that accepts stream and tell if this
  stream is local or remote (URL)
]

ext/standard/basic_functions.c
ext/standard/streamsfuncs.c
ext/standard/streamsfuncs.h
ext/standard/tests/file/include_userstream_001.phpt [new file with mode: 0755]
ext/standard/tests/file/include_userstream_002.phpt [new file with mode: 0755]
main/main.c
main/php_globals.h
main/php_streams.h
main/streams/streams.c
main/streams/userspace.c

index 0f594d49274c2f26d98cecd5c191a71add30108c..cbd989ea13e3669bc775f7f29c3030cf4fdce88d 100644 (file)
@@ -205,9 +205,10 @@ ZEND_END_ARG_INFO()
 /* }}} */
 /* {{{ 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
@@ -2346,6 +2347,11 @@ static
 ZEND_BEGIN_ARG_INFO(arginfo_stream_get_wrappers, 0)
 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) */
@@ -3596,6 +3602,7 @@ zend_function_entry basic_functions[] = { /* {{{ */
        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 327806da640a49edc399a18c7967614a6c7088d4..6aa34e95f48939b0b70d4c350b9c8439c23b1dbf 100644 (file)
@@ -1647,6 +1647,37 @@ PHP_FUNCTION(stream_resolve_include_path)
 }
 /* }}} */
 
+/* {{{ 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 45893bd9b94f3640a35b342b9d6076ecfe6572ff..5350d0fe81996f1c2aa4842e38376eed65ea9685 100644 (file)
@@ -57,6 +57,7 @@ PHP_FUNCTION(stream_socket_enable_crypto);
 PHP_FUNCTION(stream_socket_shutdown);
 PHP_FUNCTION(stream_socket_pair);
 PHP_FUNCTION(stream_resolve_include_path);
+PHP_FUNCTION(stream_is_local);
 
 /*
  * Local variables:
diff --git a/ext/standard/tests/file/include_userstream_001.phpt b/ext/standard/tests/file/include_userstream_001.phpt
new file mode 100755 (executable)
index 0000000..d805afd
--- /dev/null
@@ -0,0 +1,82 @@
+--TEST--
+User streams and include()
+--INI--
+allow_url_fopen=1
+allow_url_include=0
+--FILE--
+<?php
+class test {
+    private $data = '<?php echo "Hello World\n";?>';
+       private $pos;
+
+       function stream_open($path, $mode, $options, &$opened_path)
+       {
+               if (strchr($mode, 'a'))
+                       $this->pos = strlen($this->data);
+               else
+                       $this->po = 0;
+               
+               return true;
+       }
+
+       function stream_read($count)
+       {
+               $ret = substr($this->data, $this->pos, $count);
+               $this->pos += strlen($ret);
+               return $ret;
+       }
+
+       function stream_tell()
+       {
+               return $this->pos;
+       }
+
+       function stream_eof()
+       {
+               return $this->pos >= strlen($this->data);
+       }
+
+       function stream_seek($offset, $whence)
+       {
+               switch($whence) {
+                       case SEEK_SET:
+                               if ($offset < $this->data && $offset >= 0) {
+                                       $this->pos = $offset;
+                                       return true;
+                               } else {
+                                       return false;
+                               }
+                               break;
+                       case SEEK_CUR:
+                               if ($offset >= 0) {
+                                       $this->pos += $offset;
+                                       return true;
+                               } else {
+                                       return false;
+                               }
+                               break;
+                       case SEEK_END:
+                               if (strlen($this->data) + $offset >= 0) {
+                                       $this->pos = strlen($this->data) + $offset;
+                                       return true;
+                               } else {
+                                       return false;
+                               }
+                               break;
+                       default:
+                               return false;
+               }
+       }
+
+}
+
+stream_register_wrapper("test1", "test", STREAM_IS_URL);
+stream_register_wrapper("test2", "test");
+echo @file_get_contents("test1://hello"),"\n";
+@include "test1://hello";
+echo @file_get_contents("test2://hello"),"\n";
+@include "test2://hello";
+--EXPECT--
+<?php echo "Hello World\n";?>
+<?php echo "Hello World\n";?>
+Hello World
diff --git a/ext/standard/tests/file/include_userstream_002.phpt b/ext/standard/tests/file/include_userstream_002.phpt
new file mode 100755 (executable)
index 0000000..d422a82
--- /dev/null
@@ -0,0 +1,106 @@
+--TEST--
+local user streams must not be able to open() url's
+--INI--
+allow_url_fopen=1
+allow_url_include=0
+--FILE--
+<?php
+class test {
+    private $data = '<?php echo "Hello World\n";?>';
+       private $pos;
+       private $stream = null;
+
+       function stream_open($path, $mode, $options, &$opened_path)
+       {
+               if (strpos($path, "test2://") === 0) {
+                       $this->stream = fopen("test1://".substr($path, 8), $mode);
+                       return !empty($this->stream);
+               }
+               if (strchr($mode, 'a'))
+                       $this->pos = strlen($this->data);
+               else
+                       $this->po = 0;
+               
+               return true;
+       }
+
+       function stream_read($count)
+       {
+               if (!empty($this->stream)) {
+                       return fread($this->stream, $count);
+               }
+               $ret = substr($this->data, $this->pos, $count);
+               $this->pos += strlen($ret);
+               return $ret;
+       }
+
+       function stream_tell()
+       {
+               if (!empty($this->stream)) {
+                       return ftell($this->stream);
+               }
+               return $this->pos;
+       }
+
+       function stream_eof()
+       {
+               if (!empty($this->stream)) {
+                       return feof($this->stream);
+               }
+               return $this->pos >= strlen($this->data);
+       }
+
+       function stream_seek($offset, $whence)
+       {
+               if (!empty($this->stream)) {
+                       return fseek($this->stream, $offset, $whence);
+               }
+               switch($whence) {
+                       case SEEK_SET:
+                               if ($offset < $this->data && $offset >= 0) {
+                                       $this->pos = $offset;
+                                       return true;
+                               } else {
+                                       return false;
+                               }
+                               break;
+                       case SEEK_CUR:
+                               if ($offset >= 0) {
+                                       $this->pos += $offset;
+                                       return true;
+                               } else {
+                                       return false;
+                               }
+                               break;
+                       case SEEK_END:
+                               if (strlen($this->data) + $offset >= 0) {
+                                       $this->pos = strlen($this->data) + $offset;
+                                       return true;
+                               } else {
+                                       return false;
+                               }
+                               break;
+                       default:
+                               return false;
+               }
+       }
+
+}
+
+stream_register_wrapper("test1", "test", STREAM_IS_URL);
+stream_register_wrapper("test2", "test");
+echo @file_get_contents("test1://hello"),"\n";
+@include "test1://hello";
+echo @file_get_contents("test2://hello"),"\n";
+include "test2://hello";
+--EXPECTF--
+<?php echo "Hello World\n";?>
+<?php echo "Hello World\n";?>
+
+Warning: fopen(): test1:// wrapper is disabled in the server configuration in %sinclude_userstream_002.php on line 10
+
+Warning: fopen(test1://hello): failed to open stream: no suitable wrapper could be found in %sinclude_userstream_002.php on line 10
+
+Warning: include(test2://hello): failed to open stream: "test::stream_open" call failed in %sinclude_userstream_002.php on line 89
+
+Warning: include(): Failed opening 'test2://hello' for inclusion (include_path='%s') in %sinclude_userstream_002.php on line 89
index 7abf5d0aee9feb6bd9e58456052bdf2040ae198e..8a3b350bdb5ea44d03029df6a2d83e6bd443683d 100644 (file)
@@ -1232,6 +1232,7 @@ int php_request_startup(TSRMLS_D)
                PG(modules_activated) = 0;
                PG(header_is_being_sent) = 0;
                PG(connection_status) = PHP_CONNECTION_NORMAL;
+               PG(in_user_include) = 0;
 
                zend_activate(TSRMLS_C);
                sapi_activate(TSRMLS_C);
index 04da9289c23438edbe321f57fa5b6ca25eaaf9b4..0dbdb49fbb69c410b62aca7d646a04d5a1951fb8 100644 (file)
@@ -142,6 +142,7 @@ struct _php_core_globals {
        zend_bool com_initialized;
 #endif
        long max_input_nesting_level;
+       zend_bool in_user_include;
 };
 
 
index bcb5cad45a827e7f354129fc0083adfe9bf92c68..77808b1d4af302faca64b23bf9da3068456fe7d0 100755 (executable)
@@ -619,6 +619,9 @@ END_EXTERN_C()
 /* use glob stream for directory open in plain files stream */
 #define STREAM_USE_GLOB_DIR_OPEN        0x00001000
 
+/* use glob stream for directory open in plain files stream */
+#define STREAM_DISABLE_URL_PROTECTION   0x00002000
+
 /* Antique - no longer has meaning */
 #define IGNORE_URL_WIN 0
 
@@ -669,7 +672,7 @@ PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstr
 
 PHP_INI_MH(OnUpdateAllowUrl);
 PHPAPI int php_stream_wrapper_is_allowed(const char *wrapper, int wrapper_len, const char *setting TSRMLS_DC);
-#define php_stream_allow_url_fopen(wrapper, wrapper_len)       php_stream_wrapper_is_allowed((wrapper), (wrapper_len), PG(allow_url_fopen_list) TSRMLS_CC)
+#define php_stream_allow_url_fopen(wrapper, wrapper_len)       php_stream_wrapper_is_allowed((wrapper), (wrapper_len), PG(in_user_include)?PG(allow_url_include_list):PG(allow_url_fopen_list) TSRMLS_CC)
 #define php_stream_allow_url_include(wrapper, wrapper_len)     php_stream_wrapper_is_allowed((wrapper), (wrapper_len), PG(allow_url_include_list) TSRMLS_CC)
 
 /* Give other modules access to the url_stream_wrappers_hash and stream_filters_hash */
@@ -683,6 +686,8 @@ extern php_stream_wrapper_ops *php_stream_user_wrapper_ops;
 END_EXTERN_C()
 #endif
 
+/* Definitions for user streams */
+#define PHP_STREAM_IS_URL              1
 /*
  * Local variables:
  * tab-width: 4
index 24496d7c54c95715f57c6795b9b0f3054dcd4f52..e484b7f9cb88f724ea12b774a01cd3ccd4cf978f 100755 (executable)
@@ -2163,8 +2163,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char
                return plain_files_wrapper;
        }
 
-       if (!php_stream_allow_url_fopen(protocol, n) ||
-               ((options & STREAM_OPEN_FOR_INCLUDE) && !php_stream_allow_url_include(protocol, n)) ) {
+       if (((options & STREAM_DISABLE_URL_PROTECTION) == 0) &&
+           (!php_stream_allow_url_fopen(protocol, n) ||
+                ((options & STREAM_OPEN_FOR_INCLUDE) && !php_stream_allow_url_include(protocol, n)))) {
                if (options & REPORT_ERRORS) {
                        /* protocol[n] probably isn't '\0' */
                        char *protocol_dup = estrndup(protocol, n);
@@ -2797,7 +2798,7 @@ PHPAPI int _php_stream_path_encode(php_stream_wrapper *wrapper,
 
                                return FAILURE;
                        }
-                       wrapper = php_stream_locate_url_wrapper(scheme, NULL, options TSRMLS_CC);
+                       wrapper = php_stream_locate_url_wrapper(scheme, NULL, options | STREAM_DISABLE_URL_PROTECTION TSRMLS_CC);
                        efree(scheme);
                        if (!wrapper) {
                                *pathenc = NULL;
index 29271d72c0845125c3af1e3c323645c5584f4e5a..dfc29009425077bfe3643a601f2d3fa734c571ad 100644 (file)
@@ -81,6 +81,7 @@ PHP_MINIT_FUNCTION(user_streams)
        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_URL", PHP_STREAM_IS_URL,              CONST_CS|CONST_PERSISTENT);
        return SUCCESS;
 }
 
@@ -215,6 +216,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
        int call_result;
        php_stream *stream = NULL;
        zval *zcontext = NULL;
+       zend_bool old_in_user_include;
 
        /* 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,17 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
        }
        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 */
+       old_in_user_include = PG(in_user_include);
+       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] != '*')) {
+               PG(in_user_include) = 1;
+       }
+
        us = emalloc(sizeof(*us));
        us->wrapper = uwrap;    
 
@@ -258,6 +271,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
                        FREE_ZVAL(us->object);
                        efree(us);
                        FG(user_stream_current_filename) = NULL;
+                       PG(in_user_include) = old_in_user_include;
                        return NULL;
                } else {
                        if (retval_ptr) {
@@ -338,7 +352,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena
        zval_ptr_dtor(&zfilename);
 
        FG(user_stream_current_filename) = NULL;
-               
+
+       PG(in_user_include) = old_in_user_include;
        return stream;
 }
 
@@ -436,8 +451,9 @@ PHP_FUNCTION(stream_wrapper_register)
        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 +462,7 @@ PHP_FUNCTION(stream_wrapper_register)
        uwrap->classname = estrndup(classname, classname_len);
        uwrap->wrapper.wops = &user_stream_wops;
        uwrap->wrapper.abstract = uwrap;
+       uwrap->wrapper.is_url = ((flags & PHP_STREAM_IS_URL) != 0);
 
        rsrc_id = ZEND_REGISTER_RESOURCE(NULL, uwrap, le_protocols);