Fix XSS problem with unsanitized style tags in messages [CVE-2011-2023]
[squirrelmail.git] / src / configtest.php
index b2fbb75309347fbd8d02bb0f5e9d011ca0e0feef..2cac584c21518e34f41cbc8b4e14e0457f7ffdd3 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * SquirrelMail configtest script
  *
- * @copyright © 2003-2007 The SquirrelMail Project Team
+ * @copyright 2003-2011 The SquirrelMail Project Team
  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  * @version $Id$
  * @package squirrelmail
@@ -21,7 +21,10 @@ define('PAGE_NAME', 'configtest');
 // This script could really use some restructuring as it has grown quite rapidly
 // but is not very 'clean'. Feel free to get some structure into this thing.
 
-/** force verbose error reporting and turn on display of errors */
+// force verbose error reporting and turn on display of errors, but not before
+// getting their original values
+$php_display_errors_original_value = ini_get('display_errors');
+$php_error_reporting_original_value = ini_get('error_reporting');
 error_reporting(E_ALL);
 ini_set('display_errors',1);
 
@@ -87,6 +90,7 @@ define('SM_PATH', '../');
 require(SM_PATH . 'include/constants.php');
 require(SM_PATH . 'functions/global.php');
 require(SM_PATH . 'functions/strings.php');
+require(SM_PATH . 'functions/files.php');
 $SQM_INTERNAL_VERSION = explode('.', SM_VERSION, 3);
 $SQM_INTERNAL_VERSION[2] = intval($SQM_INTERNAL_VERSION[2]);
 
@@ -116,6 +120,12 @@ if (file_exists(SM_PATH . 'config/config_local.php')) {
     require(SM_PATH . 'config/config_local.php');
 }
 
+/**
+ * Include Compatibility plugin if available.
+ */
+if (!$disable_plugins && file_exists(SM_PATH . 'plugins/compatibility/functions.php'))
+    include_once(SM_PATH . 'plugins/compatibility/functions.php');
+
 /** Load plugins */
 global $disable_plugins;
 $squirrelmail_plugin_hooks = array();
@@ -196,12 +206,39 @@ if(!check_php_version(4,1,0)) {
 
 echo $IND . 'PHP version ' . PHP_VERSION . ' OK. (You have: ' . phpversion() . ". Minimum: 4.1.0)<br />\n";
 
-echo $IND . 'display_errors: ' . ini_get('display_errors') . "<br />\n";
-
-echo $IND . 'error_reporting: ' . ini_get('error_reporting') . "<br />\n";
+// try to determine information about the user and group the web server is running as
+//
+$webOwnerID = 'N/A';
+$webOwnerInfo = array('name' => 'N/A');
+if (function_exists('posix_getuid'))
+    $webOwnerID = posix_getuid();
+if ($webOwnerID === FALSE)
+    $webOwnerID = 'N/A';
+if ($webOwnerID !== 'N/A' && function_exists('posix_getpwuid'))
+    $webOwnerInfo = posix_getpwuid($webOwnerID);
+if (!$webOwnerInfo)
+    $webOwnerInfo = array('name' => 'N/A');
+$webGroupID = 'N/A';
+$webGroupInfo = array('name' => 'N/A');
+if (function_exists('posix_getgid'))
+    $webGroupID = posix_getgid();
+if ($webGroupID === FALSE)
+    $webGroupID = 'N/A';
+if ($webGroupID !== 'N/A' && function_exists('posix_getgrgid'))
+    $webGroupInfo = posix_getgrgid($webGroupID);
+if (!$webGroupInfo)
+    $webGroupInfo = array('name' => 'N/A');
+
+echo $IND . 'Running as ' . $webOwnerInfo['name'] . '(' . $webOwnerID
+          . ') / ' . $webGroupInfo['name'] . '(' . $webGroupID . ")<br />\n";
+
+echo $IND . 'display_errors: ' . $php_display_errors_original_value . " (overridden with 1 for this page only)<br />\n";
+
+echo $IND . 'error_reporting: ' . $php_error_reporting_original_value . " (overridden with 2047 for this page only)<br />\n";
 
 $safe_mode = ini_get('safe_mode');
 if ($safe_mode) {
+    //FIXME: should show that safe_mode is off when it is (this only shows the safe_mode setting when it's on) (also might be generally helpful to show things like open_basedir, too or even add phpinfo() output or a link to another script that has phpinfo()
     echo $IND . 'safe_mode: ' . $safe_mode;
     if (empty($prefs_dsn) || empty($addrbook_dsn))
         echo ' (<font color="red">double check data and attachment directory ownership, etc!</font>)';
@@ -216,28 +253,21 @@ if ($safe_mode) {
 
 if ((bool) ini_get('register_globals') &&
     strtolower(ini_get('register_globals'))!='off') {
-    do_err('You have register_globals turned on.  This is not an error, but it CAN be a security hazard.  Consider turning register_globals off.', false);
+    do_err('You have register_globals turned on. This is not an error, but it CAN be a security hazard. Consider turning register_globals off.', false);
 }
 
 
 /* variables_order check */
 
-// FIXME(?): Hmm, how do we distinguish between when an ini setting is
-//           not available (ini_set() returns empty string) and when
-//           the administrator set the value to an empty string?  The
-//           latter is sure to be highly rare, so for now, just assume
-//           that empty value means the setting isn't even available
-//           (could also check PHP version when this setting was implemented)
-//           although, we'll also warn the user if it is empty, with
-//           a non-fatal error
+// FIXME(?): Hmm, how do we distinguish between when an ini setting is not available (ini_set() returns empty string) and when the administrator set the value to an empty string? The latter is sure to be highly rare, so for now, just assume that empty value means the setting isn't even available (could also check PHP version when this setting was implemented) although, we'll also warn the user if it is empty, with a non-fatal error
 $variables_order = strtoupper(ini_get('variables_order'));
 if (empty($variables_order))
-    do_err('Your variables_order setting seems to be empty.  Make sure it is undefined in any PHP ini files, .htaccess files, etc. and not specifically set to an empty value or SquirrelMail may not function correctly', false);
+    do_err('Your variables_order setting seems to be empty. Make sure it is undefined in any PHP ini files, .htaccess files, etc. and not specifically set to an empty value or SquirrelMail may not function correctly', false);
 else if (strpos($variables_order, 'G') === FALSE
  || strpos($variables_order, 'P') === FALSE
  || strpos($variables_order, 'C') === FALSE
  || strpos($variables_order, 'S') === FALSE) {
-    do_err('Your variables_order setting is insufficient for SquirrelMail to function.  It needs at least "GPCS", but you have it set to "' . htmlspecialchars($variables_order) . '"', true);
+    do_err('Your variables_order setting is insufficient for SquirrelMail to function. It needs at least "GPCS", but you have it set to "' . htmlspecialchars($variables_order) . '"', true);
 } else {
     echo $IND . "variables_order OK: $variables_order.<br />\n";
 }
@@ -246,21 +276,14 @@ else if (strpos($variables_order, 'G') === FALSE
 /* gpc_order check (removed from PHP as of v5.0) */
 
 if (!check_php_version(5)) {
-    // FIXME(?): Hmm, how do we distinguish between when an ini setting is
-    //           not available (ini_set() returns empty string) and when
-    //           the administrator set the value to an empty string?  The
-    //           latter is sure to be highly rare, so for now, just assume
-    //           that empty value means the setting isn't even available
-    //           (could also check PHP version when this setting was implemented)
-    //           although, we'll also warn the user if it is empty, with
-    //           a non-fatal error
+    // FIXME(?): Hmm, how do we distinguish between when an ini setting is not available (ini_set() returns empty string) and when the administrator set the value to an empty string? The latter is sure to be highly rare, so for now, just assume that empty value means the setting isn't even available (could also check PHP version when this setting was implemented) although, we'll also warn the user if it is empty, with a non-fatal error
     $gpc_order = strtoupper(ini_get('gpc_order'));
     if (empty($gpc_order))
-        do_err('Your gpc_order setting seems to be empty.  Make sure it is undefined in any PHP ini files, .htaccess files, etc. and not specifically set to an empty value or SquirrelMail may not function correctly', false);
+        do_err('Your gpc_order setting seems to be empty. Make sure it is undefined in any PHP ini files, .htaccess files, etc. and not specifically set to an empty value or SquirrelMail may not function correctly', false);
     else if (strpos($gpc_order, 'G') === FALSE
      || strpos($gpc_order, 'P') === FALSE
      || strpos($gpc_order, 'C') === FALSE) {
-        do_err('Your gpc_order setting is insufficient for SquirrelMail to function.  It needs to be set to "GPC", but you have it set to "' . htmlspecialchars($gpc_order) . '"', true);
+        do_err('Your gpc_order setting is insufficient for SquirrelMail to function. It needs to be set to "GPC", but you have it set to "' . htmlspecialchars($gpc_order) . '"', true);
     } else {
         echo $IND . "gpc_order OK: $gpc_order.<br />\n";
     }
@@ -327,6 +350,17 @@ if (ini_get('short_open_tag') == 0) {
     do_err($short_open_tag_warning, false);
 }
 
+
+/* check who the web server is running as if possible */
+
+if ($process_info = get_process_owner_info()) {
+    echo $IND . 'Web server is running as user: ' . $process_info['name'] . ' (' . $process_info['uid'] . ")<br />\n";
+    //echo $IND . 'Web server is running as effective user: ' . $process_info['ename'] . ' (' . $process_info['euid'] . ")<br />\n";
+    echo $IND . 'Web server is running as group: ' . $process_info['group'] . ' (' . $process_info['gid'] . ")<br />\n";
+    //echo $IND . 'Web server is running as effective group: ' . $process_info['egroup'] . ' (' . $process_info['egid'] . ")<br />\n";
+}
+
+
 /* checking paths */
 
 echo "Checking paths...<br />\n";
@@ -350,7 +384,7 @@ if(!isset($data_dir_error) && !is_dir($data_dir)) {
     }
 }
 // datadir should be executable - but no clean way to test on that
-if(!isset($data_dir_error) && !is_writable($data_dir)) {
+if(!isset($data_dir_error) && !sq_is_writable($data_dir)) {
     if (!empty($prefs_dsn)) {
         $data_dir_error = "Data dir ($data_dir) is not writable!\n";
         echo $IND . '<font color="red"><b>ERROR:</b></font> ' . $data_dir_error;
@@ -378,7 +412,7 @@ if($data_dir == $attachment_dir) {
     if (!is_dir($attachment_dir)) {
         do_err("Attachment dir ($attachment_dir) is not a directory!");
     }
-    if (!is_writable($attachment_dir)) {
+    if (!sq_is_writable($attachment_dir)) {
         do_err("I cannot write to attachment dir ($attachment_dir)!");
     }
     echo $IND . "Attachment dir OK.<br />\n";
@@ -425,27 +459,61 @@ if (isset($plugins[0])) {
         if(!file_exists(SM_PATH .'plugins/'.$plugin)) {
             do_err('You have enabled the <i>'.$plugin.'</i> plugin, but I cannot find it.', FALSE);
         } elseif (!is_readable(SM_PATH .'plugins/'.$plugin.'/setup.php')) {
-            do_err('You have enabled the <i>'.$plugin.'</i> plugin, but I cannot read its setup.php file.', FALSE);
+            do_err('You have enabled the <i>'.$plugin.'</i> plugin, but I cannot locate or read its setup.php file.', FALSE);
         } elseif (in_array($plugin, $bad_plugins)) {
             do_err('You have enabled the <i>'.$plugin.'</i> plugin, which causes problems with this version of SquirrelMail. Please check the ReleaseNotes or other documentation for more information.', false);
         }
     }
+
+
     // load plugin functions
     include_once(SM_PATH . 'functions/plugin.php');
+
     // turn on output buffering in order to prevent output of new lines
     ob_start();
     foreach ($plugins as $name) {
         use_plugin($name);
+
+        // get output and remove whitespace
+        $output = trim(ob_get_contents());
+
+        // if plugin outputs more than newlines and spacing, stop script execution.
+        if (!empty($output)) {
+            $plugin_load_error = 'Some output was produced when plugin <i>' . $name . '</i> was loaded.  Usually this means there is an error in the plugin\'s setup or configuration file.  The output was: '.htmlspecialchars($output);
+            do_err($plugin_load_error);
+        }
     }
-    // get output and remove whitespace
-    $output = trim(ob_get_contents());
     ob_end_clean();
-    // if plugins output more than newlines and spacing, stop script execution.
-    if (!empty($output)) {
-//FIXME: if the output buffer is checked INSIDE the foreach loop above, we can tell the user WHICH plugin has the problem - seems like a good idea
-        $plugin_load_error = 'Some output is produced when plugins are loaded. Usually this means there is an error in one of the plugin setup or configuration files. The output was: '.htmlspecialchars($output);
-        do_err($plugin_load_error);
+
+
+    /**
+     * Check the contents of the static plugin hooks array file against
+     * the plugin setup file, which may have changed in an upgrade, etc.
+     * This helps remind admins to re-run the configuration utility when
+     * a plugin has been changed or upgraded.
+     */
+    $static_squirrelmail_plugin_hooks = $squirrelmail_plugin_hooks;
+    $squirrelmail_plugin_hooks = array();
+    foreach ($plugins as $name) {
+        $function = "squirrelmail_plugin_init_$name";
+        if (function_exists($function)) {
+            $function();
+
+            // now iterate through each hook and make sure the
+            // plugin is registered on the correct ones in the
+            // static plugin configuration file
+            //
+            foreach ($squirrelmail_plugin_hooks as $hook_name => $hooked_plugins)
+                foreach ($hooked_plugins as $hooked_plugin => $hooked_function)
+                    if ($hooked_plugin == $name
+                     && (empty($static_squirrelmail_plugin_hooks[$hook_name][$hooked_plugin])
+                      || $static_squirrelmail_plugin_hooks[$hook_name][$hooked_plugin] != $hooked_function))
+                        do_err('The plugin <i>' . $name . '</i> is supposed to be registered on the <i>' . $hook_name . '</i> hook, but it is not.  You need to re-run the configuration utility and re-save your configuration file.', FALSE);
+        }
     }
+    $squirrelmail_plugin_hooks = $static_squirrelmail_plugin_hooks;
+
+
     /**
      * Print plugin versions
      */
@@ -464,13 +532,21 @@ if (isset($plugins[0])) {
         }
         else if (is_array($failed_dependencies)) {
             $missing_plugins = '';
+            $incompatible_plugins = '';
             foreach ($failed_dependencies as $depend_name => $depend_requirements) {
-                $missing_plugins .= ', ' . $depend_name . ' (version ' . $depend_requirements['version'] . ', ' . ($depend_requirements['activate'] ? 'must be activated' : 'need not be activated') . ')';
+                if ($depend_requirements['version'] == SQ_INCOMPATIBLE)
+                    $incompatible_plugins .= ', ' . $depend_name;
+                else
+                    $missing_plugins .= ', ' . $depend_name . ' (version ' . $depend_requirements['version'] . ', ' . ($depend_requirements['activate'] ? 'must be activated' : 'need not be activated') . ')';
             }
-            do_err($name . ' is missing some dependencies: ' . trim($missing_plugins, ', '), FALSE);
+            $error_string = (!empty($incompatible_plugins) ? $name . ' cannot be activated at the same time as the following plugins: ' . trim($incompatible_plugins, ', ') : '')
+                          . (!empty($missing_plugins) ? (!empty($incompatible_plugins) ? '.  ' . $name . ' is also ' : $name . ' is ') . 'missing some dependencies: ' . trim($missing_plugins, ', ') : '');
+            do_err($error_string, FALSE);
         }
 
     }
+
+
     /**
      * This hook was added in 1.5.2 and 1.4.10. Each plugins should print an error
      * message and return TRUE if there are any errors in its setup/configuration.
@@ -573,8 +649,17 @@ if($useSendmail) {
     if ($use_smtp_tls===2) {
         // if something breaks, script should close smtp connection on exit.
 
+
+        // format EHLO argument correctly if needed
+        //
+        if (preg_match('/^\d+\.\d+\.\d+\.\d+$/', $client_ip))
+            $helohost = '[' . $client_ip . ']';
+        else // some day might add IPv6 here
+            $helohost = $client_ip;
+
+
         // say helo
-        fwrite($stream,"EHLO $client_ip\r\n");
+        fwrite($stream,"EHLO $helohost\r\n");
 
         $ehlo=array();
         $ehlo_error = false;
@@ -626,15 +711,16 @@ if($useSendmail) {
 
     /* POP before SMTP */
     if($pop_before_smtp) {
-        $stream = fsockopen($smtpServerAddress, 110, $err_no, $err_str);
+        if (empty($pop_before_smtp_host)) $pop_before_smtp_host = $smtpServerAddress;
+        $stream = fsockopen($pop_before_smtp_host, 110, $err_no, $err_str);
         if (!$stream) {
-            do_err("Error connecting to POP Server ($smtpServerAddress:110) "
+            do_err("Error connecting to POP Server ($pop_before_smtp_host:110) "
                 . $err_no . ' : ' . htmlspecialchars($err_str));
         }
 
         $tmp = fgets($stream, 1024);
         if (substr($tmp, 0, 3) != '+OK') {
-            do_err("Error connecting to POP Server ($smtpServerAddress:110)"
+            do_err("Error connecting to POP Server ($pop_before_smtp_host:110)"
                 . ' '.htmlspecialchars($tmp));
         }
         fputs($stream, 'QUIT');
@@ -931,7 +1017,7 @@ if( empty($ldap_server) ) {
                 if ( empty($param['binddn']) ) {
                     $bind = @ldap_bind($linkid);
                 } else {
-                    $bind = @ldap_bind($param['binddn'], $param['bindpw']);
+                    $bind = @ldap_bind($linkid, $param['binddn'], $param['bindpw']);
                 }
 
                 if ( $bind ) {
@@ -952,7 +1038,7 @@ echo '<hr width="75%" align="center">';
 echo '<h2 align="center">Summary</h2>';
 $footer = '<hr width="75%" align="center">';
 if ($warnings) {
-    echo '<p>No fatal errors were found, but there was at least 1 warning.  Please check the flagged issue(s) carefully, as correcting them may prevent erratic, undefined, or incorrect behavior (or flat out breakage).</p>';
+    echo '<p>No fatal errors were found, but there was at least 1 warning. Please check the flagged issue(s) carefully, as correcting them may prevent erratic, undefined, or incorrect behavior (or flat out breakage).</p>';
     echo $footer;
 } else {
     print <<< EOF