summaryrefslogtreecommitdiff
path: root/main/streams/streams.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/streams/streams.c')
-rwxr-xr-xmain/streams/streams.c275
1 files changed, 259 insertions, 16 deletions
diff --git a/main/streams/streams.c b/main/streams/streams.c
index b521dcae86..13d125f2e5 100755
--- a/main/streams/streams.c
+++ b/main/streams/streams.c
@@ -2096,6 +2096,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char
}
/* TODO: curl based streams probably support file:// properly */
if (!protocol || !strncasecmp(protocol, "file", n)) {
+ /* fall back on regular file access */
+ php_stream_wrapper *plain_files_wrapper = &php_plain_files_wrapper;
+
if (protocol) {
int localhost = 0;
@@ -2132,32 +2135,37 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, char
return NULL;
}
+ /* The file:// wrapper may have been disabled/overridden */
if (FG(stream_wrappers)) {
- /* The file:// wrapper may have been disabled/overridden */
-
- if (wrapperpp) {
- /* It was found so go ahead and provide it */
- return *wrapperpp;
- }
-
- /* Check again, the original check might have not known the protocol name */
- if (zend_hash_find(wrapper_hash, "file", sizeof("file"), (void**)&wrapperpp) == SUCCESS) {
- return *wrapperpp;
+ if (!wrapperpp || zend_hash_find(wrapper_hash, "file", sizeof("file"), (void**)&wrapperpp) == FAILURE) {
+ if (options & REPORT_ERRORS) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Plainfiles wrapper disabled");
+ }
+ return NULL;
}
+ /* Handles overridden plain files wrapper */
+ plain_files_wrapper = *wrapperpp;
+ }
+
+ if (!php_stream_allow_url_fopen("file", sizeof("file") - 1) ||
+ ((options & STREAM_OPEN_FOR_INCLUDE) && !php_stream_allow_url_include("file", sizeof("file") - 1)) ) {
if (options & REPORT_ERRORS) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Plainfiles wrapper disabled");
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "file:// wrapper is disabled in the server configuration");
}
return NULL;
}
-
- /* fall back on regular file access */
- return &php_plain_files_wrapper;
+
+ return plain_files_wrapper;
}
- if ((wrapperpp && (*wrapperpp)->is_url) && (!PG(allow_url_fopen) || ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include))) ) {
+ if (!php_stream_allow_url_fopen(protocol, n) ||
+ ((options & STREAM_OPEN_FOR_INCLUDE) && !php_stream_allow_url_include(protocol, n)) ) {
if (options & REPORT_ERRORS) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "URL file-access is disabled in the server configuration");
+ /* protocol[n] probably isn't '\0' */
+ char *protocol_dup = estrndup(protocol, n);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s:// wrapper is disabled in the server configuration", protocol_dup);
+ efree(protocol_dup);
}
return NULL;
}
@@ -2866,6 +2874,241 @@ PHPAPI int _php_stream_path_decode(php_stream_wrapper *wrapper,
}
/* }}} */
+/* {{{ allow_url_fopen / allow_url_include Handlers */
+
+PHPAPI int php_stream_wrapper_is_allowed(const char *wrapper, int wrapper_len, const char *setting TSRMLS_DC)
+{
+ HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
+ php_stream_wrapper **wrapperpp;
+ int setting_len = setting ? strlen(setting) : 0;
+ const char *s = setting, *e = s + setting_len;
+ char *wrapper_dup;
+
+ /* BC: allow_url_* == on */
+ if (setting_len == 1 && *setting == '*') {
+ /* "*" means everything is allowed */
+ return 1;
+ }
+
+ if (wrapper_len == (sizeof("zlib") - 1) && strncasecmp("zlib", wrapper, sizeof("zlib") - 1) == 0) {
+ wrapper = "compress.zlib";
+ wrapper_len = sizeof("compress.zlib") - 1;
+ }
+
+ wrapper_dup = estrndup(wrapper, wrapper_len);
+ php_strtolower(wrapper_dup, wrapper_len);
+ if (FAILURE == zend_hash_find(wrapper_hash, wrapper_dup, wrapper_len + 1, (void**)&wrapperpp)) {
+ /* Wrapper does not exist, assume disallow */
+ efree(wrapper_dup);
+ return 0;
+ }
+ efree(wrapper_dup);
+
+ /* BC: allow_url_* == off */
+ if (!setting || !setting_len) {
+ /* NULL or empty indicates that only is_url == 0 wrappers are allowed */
+
+ if (wrapper_len == (sizeof("file") - 1) && strncasecmp("file", wrapper, sizeof("file") - 1) == 0) {
+ /* file:// is non-url */
+ return 1;
+ }
+
+ if ((*wrapperpp)->is_url) {
+ /* is_url types are disabled, but this is an is_url wrapper, disallow */
+ return 0;
+ }
+
+ /* Wrapper is not is_url, allow it */
+ return 1;
+ }
+
+ /* Otherwise, scan list */
+ while (s < e) {
+ const char *p = php_memnstr((char*)s, ":", 1, (char*)e);
+
+ if (!p) {
+ p = e;
+ }
+
+ if (wrapper_len == (p - s) &&
+ strncasecmp(s, wrapper, p - s) == 0) {
+ /* wrapper found in list */
+ return 1;
+ }
+
+ if ((*wrapperpp)->wops == php_stream_user_wrapper_ops &&
+ (sizeof("user") - 1) == (p - s) &&
+ strncasecmp(s, "user", sizeof("user") - 1) == 0) {
+ /* Wrapper is userspace wrapper and meta-wrapper "user" is enabled */
+ return 1;
+ }
+
+ s = p + 1;
+ }
+
+ return 0;
+}
+
+/* allow_url_*_list accepts:
+ *
+ * 1/on to enable all URL prefixes
+ * 0/off to disable all is_url=1 wrappers
+ * A colon delimited list of wrappers to allow (wildcards allowed)
+ * e.g. file:gzip:compress.*:php
+ */
+PHP_INI_MH(OnUpdateAllowUrl)
+{
+#ifndef ZTS
+ char *base = (char *) mh_arg2;
+#else
+ char *base = (char *) ts_resource(*((int *) mh_arg2));
+#endif
+ char **allow = (char **) (base+(size_t) mh_arg1);
+
+ /* BC Enable */
+ if ((new_value_length == 1 && *new_value == '1') ||
+ (new_value_length == (sizeof("on") - 1) && strncasecmp(new_value, "on", sizeof("on") - 1) == 0) ) {
+
+ if (*allow && strcmp(*allow, "*") == 0) {
+ /* Turning on, but that's no change from current, so leave it alone */
+ return SUCCESS;
+ }
+
+ if (stage != PHP_INI_STAGE_STARTUP) {
+ /* Not already on, and not in SYSTEM context, fail */
+ return FAILURE;
+ }
+
+ /* Otherwise, turn on setting */
+ if (*allow) {
+ free(*allow);
+ }
+
+ *allow = zend_strndup("*", 1);
+
+ return SUCCESS;
+ }
+
+ /* BC disable */
+ if ((new_value_length == 1 && *new_value == '0') ||
+ (new_value_length == (sizeof("off") - 1) && strncasecmp(new_value, "off", sizeof("off") - 1) == 0) ) {
+
+ /* Always permit shutting off allowurl settings */
+ if (*allow) {
+ free(*allow);
+ }
+ *allow = NULL;
+
+ return SUCCESS;
+ }
+
+ /* Specify as list */
+ if (stage == PHP_INI_STAGE_STARTUP) {
+ /* Always allow new settings in startup stage */
+ if (*allow) {
+ free(*allow);
+ }
+ *allow = zend_strndup(new_value, new_value_length);
+
+ return SUCCESS;
+ }
+
+ /* In PERDIR/RUNTIME context, do more work to ensure we're only tightening the restriction */
+
+ if (*allow && strcmp(*allow, "*") == 0) {
+ /* Currently allowing everying, so whatever we set it to will be more restrictive */
+ free(*allow);
+ *allow = zend_strndup(new_value, new_value_length);
+
+ return SUCCESS;
+ }
+
+ if (!*allow) {
+ /* Currently allowing anything with is_url == 0
+ * So long as this list doesn't contain any is_url == 1, allow it
+ */
+ HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash);
+ char *s = new_value, *e = new_value + new_value_length;
+
+ while (s < e) {
+ php_stream_wrapper **wrapper;
+ char *p = php_memnstr(s, ":", 1, e);
+ char *scan;
+ int scan_len;
+
+ if (!p) {
+ p = e;
+ }
+
+ /* file:// is never a URL */
+ if ( (p - s) == (sizeof("file") - 1) && strncasecmp(s, "file", sizeof("file") - 1) == 0 ) {
+ /* file is not a URL */
+ s = p + 1;
+ continue;
+ }
+
+ if ( (p - s) == (sizeof("zlib") - 1) && strncasecmp(s, "zlib", sizeof("zlib") - 1) == 0 ) {
+ /* Wastful since we know that compress.zlib is already lower cased, but forgivable */
+ scan = estrndup("compress.zlib", sizeof("compress.zlib") - 1);
+ scan_len = sizeof("compress.zlib") - 1;
+ } else {
+ scan = estrndup(s, p - s);;
+ scan_len = p - s;
+ php_strtolower(scan, scan_len);
+ }
+
+ if (FAILURE == zend_hash_find(wrapper_hash, scan, scan_len + 1, (void**) &wrapper)) {
+ /* Unknown wrapper, not allowed in this context */
+ efree(scan);
+ return FAILURE;
+ }
+ efree(scan);
+
+ if ((*wrapper)->is_url) {
+ /* Disallowed is_url wrapper specified when trying to escape is_url == 0 context */
+ return FAILURE;
+ }
+
+ /* Seems alright so far... */
+ s = p+1;
+ }
+
+ /* All tests passed, allow it */
+ *allow = zend_strndup(new_value, new_value_length);
+
+ return SUCCESS;
+ }
+
+ /* The current allows are restricted to a specific list,
+ * Make certain that our new list is a subset of that list
+ */
+ {
+ char *s = new_value, *e = new_value + new_value_length;
+
+ while (s < e) {
+ char *p = php_memnstr(s, ":", 1, e);
+
+ if (!p) {
+ p = e;
+ }
+
+ if (!php_stream_wrapper_is_allowed(s, p - s, *allow TSRMLS_CC)) {
+ /* Current settings don't allow this wrapper, deny */
+ return FAILURE;
+ }
+
+ s = p + 1;
+ }
+
+ free(*allow);
+ *allow = zend_strndup(new_value, new_value_length);
+
+ return SUCCESS;
+ }
+}
+
+/* }}} */
+
/*
* Local variables:
* tab-width: 4