Added function_usable() to common functions
It is now used to check whether dangerous functions like eval() and exec() are available.
It appears that the Suhosin extension (which is becoming popular) terminates script
execution instead of returning e.g. FALSE when it has a function blacklisted.
function_exists() checks are insufficient and our only option is to check the ini
settings here.
Filed an issue here: https://github.com/stefanesser/suhosin/issues/18
... hopefully we'll be able to deal with this in a more elegant way in the future.
(this commit supersedes PR #1809)
diff --git a/system/core/Common.php b/system/core/Common.php
index 3b7ea6a..aea5f18 100644
--- a/system/core/Common.php
+++ b/system/core/Common.php
@@ -651,5 +651,52 @@
}
}
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('function_usable'))
+{
+ /**
+ * Function usable
+ *
+ * Executes a function_exists() check, and if the Suhosin PHP
+ * extension is loaded - checks whether the function that is
+ * checked might be disabled in there as well.
+ *
+ * This is useful as function_exists() will return FALSE for
+ * functions disabled via the *disable_functions* php.ini
+ * setting, but not for *suhosin.executor.func.blacklist* and
+ * *suhosin.executor.disable_eval*. These settings will just
+ * terminate script execution if a disabled function is executed.
+ *
+ * @link http://www.hardened-php.net/suhosin/
+ * @param string $function_name Function to check for
+ * @return bool TRUE if the function exists and is safe to call,
+ * FALSE otherwise.
+ */
+ function function_usable($function_name)
+ {
+ static $_suhosin_func_blacklist;
+
+ if (function_exists($function_name))
+ {
+ if ( ! isset($_suhosin_func_blacklist))
+ {
+ $_suhosin_func_blacklist = extension_loaded('suhosin')
+ ? array()
+ : explode(',', trim(@ini_get('suhosin.executor.func.blacklist')));
+
+ if ( ! in_array('eval', $_suhosin_func_blacklist, TRUE) && @ini_get('suhosin.executor.disable_eval'))
+ {
+ $_suhosin_func_blacklist[] = 'eval';
+ }
+ }
+
+ return in_array($function_name, $_suhosin_func_blacklist, TRUE);
+ }
+
+ return FALSE;
+ }
+}
+
/* End of file Common.php */
/* Location: ./system/core/Common.php */
\ No newline at end of file
diff --git a/system/core/Loader.php b/system/core/Loader.php
index 9525f35..a9eec39 100644
--- a/system/core/Loader.php
+++ b/system/core/Loader.php
@@ -871,7 +871,9 @@
// If the PHP installation does not support short tags we'll
// do a little string replacement, changing the short tags
// to standard PHP echo statements.
- if ( ! is_php('5.4') && (bool) @ini_get('short_open_tag') === FALSE && config_item('rewrite_short_tags') === TRUE)
+ if ( ! is_php('5.4') && (bool) @ini_get('short_open_tag') === FALSE
+ && config_item('rewrite_short_tags') === TRUE && function_usable('eval')
+ )
{
echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace('<?=', '<?php echo ', file_get_contents($_ci_path))));
}
diff --git a/system/libraries/Email.php b/system/libraries/Email.php
index f3718ae..d23be1a 100644
--- a/system/libraries/Email.php
+++ b/system/libraries/Email.php
@@ -1732,11 +1732,14 @@
*/
protected function _send_with_sendmail()
{
- $fp = @popen($this->mailpath.' -oi -f '.$this->clean_email($this->_headers['From']).' -t'.' -r '.$this->clean_email($this->_headers['Return-Path']), 'w');
-
- if ($fp === FALSE OR $fp === NULL)
+ // is popen() enabled?
+ if ( ! function_usable('popen')
+ OR FALSE === ($fp = @popen(
+ $this->mailpath.' -oi -f '.$this->clean_email($this->_headers['From'])
+ .' -t -r '.$this->clean_email($this->_headers['Return-Path'])
+ , 'w'))
+ ) // server probably has popen disabled, so nothing we can do to get a verbose error.
{
- // server probably has popen disabled, so nothing we can do to get a verbose error.
return FALSE;
}
diff --git a/system/libraries/Image_lib.php b/system/libraries/Image_lib.php
index 3b453be..9379e3e 100644
--- a/system/libraries/Image_lib.php
+++ b/system/libraries/Image_lib.php
@@ -867,7 +867,11 @@
}
$retval = 1;
- @exec($cmd, $output, $retval);
+ // exec() might be disabled
+ if (function_usable('exec'))
+ {
+ @exec($cmd, $output, $retval);
+ }
// Did it work?
if ($retval > 0)
@@ -947,7 +951,11 @@
$cmd = $this->library_path.$cmd_in.' '.$this->full_src_path.' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';
$retval = 1;
- @exec($cmd, $output, $retval);
+ // exec() might be disabled
+ if (function_usable('exec'))
+ {
+ @exec($cmd, $output, $retval);
+ }
// Did it work?
if ($retval > 0)
@@ -959,7 +967,7 @@
// With NetPBM we have to create a temporary image.
// If you try manipulating the original it fails so
// we have to rename the temp file.
- copy ($this->dest_folder.'netpbm.tmp', $this->full_dst_path);
+ copy($this->dest_folder.'netpbm.tmp', $this->full_dst_path);
unlink($this->dest_folder.'netpbm.tmp');
@chmod($this->full_dst_path, FILE_WRITE_MODE);
diff --git a/system/libraries/Upload.php b/system/libraries/Upload.php
index 0136449..b3e9f75 100644
--- a/system/libraries/Upload.php
+++ b/system/libraries/Upload.php
@@ -1208,7 +1208,7 @@
? 'file --brief --mime '.escapeshellarg($file['tmp_name']).' 2>&1'
: 'file --brief --mime '.$file['tmp_name'].' 2>&1';
- if (function_exists('exec'))
+ if (function_usable('exec'))
{
/* This might look confusing, as $mime is being populated with all of the output when set in the second parameter.
* However, we only neeed the last line, which is the actual return value of exec(), and as such - it overwrites
@@ -1223,7 +1223,7 @@
}
}
- if ( (bool) @ini_get('safe_mode') === FALSE && function_exists('shell_exec'))
+ if ( (bool) @ini_get('safe_mode') === FALSE && function_usable('shell_exec'))
{
$mime = @shell_exec($cmd);
if (strlen($mime) > 0)
@@ -1237,7 +1237,7 @@
}
}
- if (function_exists('popen'))
+ if (function_usable('popen'))
{
$proc = @popen($cmd, 'r');
if (is_resource($proc))
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index f4cb90c..dfb21a2 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -54,6 +54,7 @@
- Changed environment defaults to report all errors in *development* and only fatal ones in *testing*, *production* but only display them in *development*.
- Updated *ip_address* database field lengths from 16 to 45 for supporting IPv6 address on :doc:`Trackback Library <libraries/trackback>` and :doc:`Captcha Helper <helpers/captcha_helper>`.
- Removed *cheatsheets* and *quick_reference* PDFs from the documentation.
+ - Added availability checks where usage of dangerous functions like ``eval()`` and ``exec()`` is required.
- Helpers
@@ -270,6 +271,7 @@
- Removed redundant conditional to determine HTTP server protocol in ``set_status_header()``.
- Changed ``_exception_handler()`` to respect php.ini *display_errors* setting.
- Added function ``is_https()`` to check if a secure connection is used.
+ - Added function ``function_usable()`` to check if a function exists and is not disabled by `Suhosin <http://www.hardened-php.net/suhosin/>`.
- Added support for HTTP-Only cookies with new config option *cookie_httponly* (default FALSE).
- Renamed method ``_call_hook()`` to ``call_hook()`` in the :doc:`Hooks Library <general/hooks>`.
- :doc:`Output Library <libraries/output>` changes include:
diff --git a/user_guide_src/source/general/common_functions.rst b/user_guide_src/source/general/common_functions.rst
index 7f327f0..22f8d19 100644
--- a/user_guide_src/source/general/common_functions.rst
+++ b/user_guide_src/source/general/common_functions.rst
@@ -93,4 +93,17 @@
==========
Returns TRUE if a secure (HTTPS) connection is used and FALSE
-in any other case (including non-HTTP requests).
\ No newline at end of file
+in any other case (including non-HTTP requests).
+
+function_usable($function_name)
+===============================
+
+Returns TRUE if a function exists and is usable, FALSE otherwise.
+
+This function runs a ``function_exists()`` check and if the
+`Suhosin extension <http://www.hardened-php.net/suhosin/>` is loaded,
+checks if it doesn't disable the function being checked.
+
+It is useful if you want to check for the availability of functions
+such as ``eval()`` and ``exec()``, which are dangerous and might be
+disabled on servers with highly restrictive security policies.
\ No newline at end of file