diff --git a/web/.htaccess b/web/.htaccess
index c5dbb5b4c..8ef4c4981 100644
--- a/web/.htaccess
+++ b/web/.htaccess
@@ -58,6 +58,16 @@ DirectoryIndex index.php
php_value mbstring.encoding_translation 0
+# PHP 7, Apache 1 and 2.
+
+ php_value magic_quotes_gpc 0
+ php_value register_globals 0
+ php_value session.auto_start 0
+ php_value mbstring.http_input pass
+ php_value mbstring.http_output pass
+ php_value mbstring.encoding_translation 0
+
+
# Requires mod_expires to be enabled.
# Enable expirations.
diff --git a/web/CHANGELOG.txt b/web/CHANGELOG.txt
index e6b38ab8b..6c49315ac 100644
--- a/web/CHANGELOG.txt
+++ b/web/CHANGELOG.txt
@@ -1,23 +1,39 @@
+Drupal 6.38-p6, 2018-12-28 - SA-CORE-2018-006
+---------------------------------------------
+
+Code brought up-to-date with Pressflow 6.46.126 and Drupal 6.46 LTS, including
+a backport of the security fix for an open redirect exploit.
+
+The path module allows users with the 'administer paths' to create pretty URLs
+for content. In certain circumstances the user can enter a particular path that
+triggers an open redirect to a malicious url. The issue is mitigated by the fact
+that the user needs the administer paths permission to exploit.
+
+Upstream reference:
+https://www.drupal.org/sa-core-2018-006
+
+
Drupal 6.38-p5, 2018-04-25 - SA-CORE-2018-004
+---------------------------------------------
A remote code execution vulnerability may exist within multiple subsystems of
Drupal 6. This potentially allows attackers to exploit multiple attack vectors
on a Drupal site, which could result in the site being compromised. This
vulnerability is related to Drupal core - Highly critical - Remote Code
-Execution - SA-CORE-2018-002 (fixed by Tag1 Quo -p3 and -p4). While
-SA-CORE-2018-002 is being exploited in the wild, there are not yet any known
-exploits for SA-CORE-2018-004.
+Execution - SA-CORE-2018-002 (fixed by Tag1 Quo -p3 and -p4).
Upstream reference:
https://www.drupal.org/sa-core-2018-004
Drupal 6.38-p4, 2018-03-28 - SA-CORE-2018-002
+---------------------------------------------
A bugfix introduced in -p3, affecting sites using PHP 5.x.
Drupal 6.38-p3, 2018-03-28 - SA-CORE-2018-002
+---------------------------------------------
A remote code execution vulnerability exists within multiple subsystems.
This potentially allows attackers to exploit multiple attack vectors on a
@@ -71,6 +87,7 @@ Almost all site administrators will want to take the infrastructure
actions suggested in httppoxy.org and a complete solution to this
vulnerability.
+
Drupal 6.38, 2016-02-24 - Final release
---------------------------------------
- Fixed security issues (multiple vulnerabilities). See SA-CORE-2016-001.
diff --git a/web/includes/bootstrap.inc b/web/includes/bootstrap.inc
index 6d695b173..9ad12f386 100644
--- a/web/includes/bootstrap.inc
+++ b/web/includes/bootstrap.inc
@@ -790,7 +790,7 @@ function drupal_page_is_cacheable($force = NULL) {
$result = $forced_cache
&& !drupal_session_started()
&& ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')
- && !count(drupal_get_messages(NULL, FALSE))
+ && !drupal_get_messages(NULL, FALSE)
&& !drupal_is_cli();
return $result;
@@ -1921,7 +1921,12 @@ function drupal_session_regenerate() {
if (drupal_session_started()) {
$old_session_id = session_id();
+ // On PHP7, sess_read($key) is being called with the new session key but not
+ // on PHP5. This failsafe stores the initial user object, which on PHP5 should
+ // be just the same, and restore it later on.
+ $account = $user;
session_regenerate_id();
+ $user = $account;
}
else {
// Start the session when it doesn't exist yet.
@@ -2404,3 +2409,30 @@ function _drupal_parse_url($url) {
}
return $options;
}
+
+// Shim for ereg() family of functions for PHP 7+ where they don't exist.
+if (!function_exists('ereg')) {
+ function ereg($pattern, $subject, &$matches = array()) {
+ return preg_match('/' . $pattern . '/', $subject, $matches);
+ }
+
+ function eregi($pattern, $subject, &$matches = array()) {
+ return preg_match('/' . $pattern . '/i', $subject, $matches);
+ }
+
+ function ereg_replace($pattern, $replacement, $string) {
+ return preg_replace('/' . $pattern . '/', $replacement, $string);
+ }
+
+ function eregi_replace($pattern, $replacement, $string) {
+ return preg_replace('/' . $pattern . '/i', $replacement, $string);
+ }
+
+ function split($pattern, $subject, $limit = -1) {
+ return preg_split('/' . $pattern . '/', $subject, $limit);
+ }
+
+ function spliti($pattern, $subject, $limit = -1) {
+ return preg_split('/' . $pattern . '/i', $subject, $limit);
+ }
+}
diff --git a/web/includes/common.inc b/web/includes/common.inc
index b3e0949fe..d37016360 100644
--- a/web/includes/common.inc
+++ b/web/includes/common.inc
@@ -1540,6 +1540,10 @@ function url($path = NULL, $options = array()) {
}
elseif (!empty($path) && !$options['alias']) {
$path = drupal_get_path_alias($path, isset($options['language']) ? $options['language']->language : '');
+ // Strip leading slashes from internal paths to prevent them becoming external
+ // URLs without protocol. /example.com should not be turned into
+ // //example.com.
+ $path = ltrim($path, '/');
}
if (function_exists('custom_url_rewrite_outbound')) {
diff --git a/web/includes/file.inc b/web/includes/file.inc
index 96a140d91..bfa358389 100644
--- a/web/includes/file.inc
+++ b/web/includes/file.inc
@@ -236,6 +236,9 @@ SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
php_flag engine off
+
+ php_flag engine off
+
# PHP 4, Apache 1.
php_flag engine off
diff --git a/web/includes/form.inc b/web/includes/form.inc
index b9e066d6b..be58955e2 100644
--- a/web/includes/form.inc
+++ b/web/includes/form.inc
@@ -737,7 +737,7 @@ function _form_validate($elements, &$form_state, $form_id = NULL) {
// A simple call to empty() will not cut it here as some fields, like
// checkboxes, can return a valid value of '0'. Instead, check the
// length if it's a string, and the item count if it's an array.
- if ($elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0))) {
+ if ($elements['#required'] && (is_array($elements['#value']) && !count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0))) {
form_error($elements, $t('!name field is required.', array('!name' => $elements['#title'])));
}
@@ -1426,6 +1426,12 @@ function form_set_value($form_item, $value, &$form_state) {
function _form_set_value(&$form_values, $form_item, $parents, $value) {
$parent = array_shift($parents);
if (empty($parents)) {
+ // This makes PHP 7 have the same behavior as PHP 5 when the value is an
+ // empty string, rather than an array. This is depended on surprisingly
+ // often in Drupal 6 contrib.
+ if ($form_values === '') {
+ $form_values = array();
+ }
$form_values[$parent] = $value;
}
else {
diff --git a/web/includes/install.inc b/web/includes/install.inc
index 171ba736c..cb149ceec 100644
--- a/web/includes/install.inc
+++ b/web/includes/install.inc
@@ -236,6 +236,11 @@ function drupal_rewrite_settings($settings = array(), $prefix = '') {
if ($fp && fwrite($fp, $buffer) === FALSE) {
drupal_set_message(st('Failed to modify %settings, please verify the file permissions.', array('%settings' => $settings_file)), 'error');
}
+ fclose($fp);
+ // Invalidate the cache of the settings file so next request read the newly modified one
+ if (function_exists('opcache_invalidate')) {
+ opcache_invalidate($settings_file, TRUE);
+ }
}
else {
drupal_set_message(st('Failed to open %settings, please verify the file permissions.', array('%settings' => $default_settings)), 'error');
diff --git a/web/includes/install.mysql.inc b/web/includes/install.mysql.inc
index 73aa365b0..d08e72ba6 100644
--- a/web/includes/install.mysql.inc
+++ b/web/includes/install.mysql.inc
@@ -31,6 +31,7 @@ function drupal_test_mysql($url, &$success) {
$url['pass'] = isset($url['pass']) ? urldecode($url['pass']) : '';
$url['host'] = urldecode($url['host']);
$url['path'] = urldecode($url['path']);
+ $url['port'] = isset($url['port']) ? $url['port'] : NULL;
// Allow for non-standard MySQL port.
if (isset($url['port'])) {
diff --git a/web/includes/install.mysqli.inc b/web/includes/install.mysqli.inc
index 008080416..ad49ab28f 100644
--- a/web/includes/install.mysqli.inc
+++ b/web/includes/install.mysqli.inc
@@ -31,6 +31,7 @@ function drupal_test_mysqli($url, &$success) {
$url['pass'] = isset($url['pass']) ? urldecode($url['pass']) : '';
$url['host'] = urldecode($url['host']);
$url['path'] = urldecode($url['path']);
+ $url['port'] = isset($url['port']) ? $url['port'] : NULL;
$connection = mysqli_init();
@mysqli_real_connect($connection, $url['host'], $url['user'], $url['pass'], substr($url['path'], 1), $url['port'], NULL, MYSQLI_CLIENT_FOUND_ROWS);
diff --git a/web/includes/unicode.inc b/web/includes/unicode.inc
index b1ce75760..bf0ae52c9 100644
--- a/web/includes/unicode.inc
+++ b/web/includes/unicode.inc
@@ -317,6 +317,25 @@ function _mime_header_decode($matches) {
return $data;
}
+/**
+ * Simple class to use instead of closure in decode_entities().
+ *
+ * @see decode_entities()
+ */
+class DrupalDecodeEntitiesCallback {
+ public $html_entities;
+ public $exclude;
+
+ public function __construct($html_entities, $exclude) {
+ $this->html_entities = $html_entities;
+ $this->exclude = $exclude;
+ }
+
+ public function callback($matches) {
+ return _decode_entities($matches[1], $matches[2], $matches[0], $this->html_entities, $this->exclude);
+ }
+}
+
/**
* Decodes all HTML entities (including numerical ones) to regular UTF-8 bytes.
*
@@ -342,11 +361,12 @@ function decode_entities($text, $exclude = array()) {
// Flip the exclude list so that we can do quick lookups later.
$exclude = array_flip($exclude);
+ // Use object instead of closure to retain PHP 5.2 compatibility.
+ $callback = new DrupalDecodeEntitiesCallback($html_entities, $exclude);
+
// Use a regexp to select all entities in one pass, to avoid decoding
- // double-escaped entities twice. The PREG_REPLACE_EVAL modifier 'e' is
- // being used to allow for a callback (see
- // http://php.net/manual/en/reference.pcre.pattern.modifiers).
- return preg_replace('/&(#x?)?([A-Za-z0-9]+);/e', '_decode_entities("$1", "$2", "$0", $html_entities, $exclude)', $text);
+ // double-escaped entities twice.
+ return preg_replace_callback('/&(#x?)?([A-Za-z0-9]+);/', array($callback, 'callback'), $text);
}
/**
diff --git a/web/install.php b/web/install.php
index 8aea0eacc..9a7821658 100644
--- a/web/install.php
+++ b/web/install.php
@@ -655,7 +655,7 @@ function install_tasks($profile, $task) {
global $base_url, $install_locale;
// Bootstrap newly installed Drupal, while preserving existing messages.
- $messages = isset($_SESSION['messages']) ? $_SESSION['messages'] : '';
+ $messages = isset($_SESSION['messages']) ? $_SESSION['messages'] : array();
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
$_SESSION['messages'] = $messages;
diff --git a/web/modules/openid/openid.install b/web/modules/openid/openid.install
index 8f1013621..0b0c95d29 100644
--- a/web/modules/openid/openid.install
+++ b/web/modules/openid/openid.install
@@ -142,6 +142,68 @@ function openid_update_6000() {
return $ret;
}
+/**
+ * Bind associations to their providers.
+ */
+function openid_update_6001() {
+ $ret = array();
+
+ db_drop_table($ret, 'openid_association');
+
+ $schema['openid_association'] = array(
+ 'description' => 'Stores temporary shared key association information for OpenID authentication.',
+ 'fields' => array(
+ 'idp_endpoint_uri' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'Primary Key: URI of the OpenID Provider endpoint.',
+ ),
+ 'assoc_handle' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'description' => 'Used to refer to this association in subsequent messages.',
+ ),
+ 'assoc_type' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'description' => 'The signature algorithm used: one of HMAC-SHA1 or HMAC-SHA256.',
+ ),
+ 'session_type' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'description' => 'Valid association session types: "no-encryption", "DH-SHA1", and "DH-SHA256".',
+ ),
+ 'mac_key' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'description' => 'The MAC key (shared secret) for this association.',
+ ),
+ 'created' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'UNIX timestamp for when the association was created.',
+ ),
+ 'expires_in' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'The lifetime, in seconds, of this association.',
+ ),
+ ),
+ 'primary key' => array('idp_endpoint_uri'),
+ 'unique keys' => array(
+ 'assoc_handle' => array('assoc_handle'),
+ ),
+ );
+
+ db_create_table($ret, 'openid_association', $schema['openid_association']);
+
+ return $ret;
+}
+
/**
* @} End of "addtogroup updates-6.x-extra".
* The next series of updates should start at 7000.
diff --git a/web/modules/statistics/statistics.module b/web/modules/statistics/statistics.module
index ba964ef15..b15380e2c 100644
--- a/web/modules/statistics/statistics.module
+++ b/web/modules/statistics/statistics.module
@@ -176,7 +176,7 @@ function statistics_user($op, &$edit, &$user) {
* Implementation of hook_cron().
*/
function statistics_cron() {
- $statistics_timestamp = variable_get('statistics_day_timestamp', '');
+ $statistics_timestamp = variable_get('statistics_day_timestamp', 0);
if ((time() - $statistics_timestamp) >= 86400) {
// Reset day counts.
diff --git a/web/modules/system/system.module b/web/modules/system/system.module
index bbe3f7754..7826a21f1 100644
--- a/web/modules/system/system.module
+++ b/web/modules/system/system.module
@@ -8,7 +8,7 @@
/**
* The current system version.
*/
-define('VERSION', '6.38-p5');
+define('VERSION', '6.46');
/**
* Core API compatibility.
diff --git a/web/modules/taxonomy/taxonomy.pages.inc b/web/modules/taxonomy/taxonomy.pages.inc
index 99757b169..56460c308 100644
--- a/web/modules/taxonomy/taxonomy.pages.inc
+++ b/web/modules/taxonomy/taxonomy.pages.inc
@@ -29,6 +29,7 @@ function taxonomy_term_page($str_tids = '', $depth = 0, $op = 'page') {
switch ($op) {
case 'page':
+ $current = new stdClass();
// Build breadcrumb based on first hierarchy of first term:
$current->tid = $tids[0];
$breadcrumb = array();
diff --git a/web/modules/upload/upload.module b/web/modules/upload/upload.module
index ee5b127dd..cb48c9648 100644
--- a/web/modules/upload/upload.module
+++ b/web/modules/upload/upload.module
@@ -278,7 +278,7 @@ function upload_nodeapi(&$node, $op, $teaser = NULL) {
case 'load':
$output = '';
if (variable_get("upload_$node->type", 1) == 1) {
- $output['files'] = upload_load($node);
+ $output = array('files' => upload_load($node));
return $output;
}
break;
diff --git a/web/sites/default/default.settings.php b/web/sites/default/default.settings.php
index 497bf1112..fc905a8fa 100644
--- a/web/sites/default/default.settings.php
+++ b/web/sites/default/default.settings.php
@@ -160,7 +160,6 @@
ini_set('session.cache_limiter', 'none');
ini_set('session.cookie_lifetime', 2000000);
ini_set('session.gc_maxlifetime', 200000);
-ini_set('session.save_handler', 'user');
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.use_trans_sid', 0);