From 609a3352e2b0a683a4fc508680bd9c89a4b53985 Mon Sep 17 00:00:00 2001 From: Jennifer Hodgdon Date: Fri, 4 Jan 2013 09:27:31 -0800 Subject: [PATCH 01/14] Issue #1869642 by kerasai: Fix placeholder documentation in batch API progress message --- includes/form.inc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/form.inc b/includes/form.inc index 514641d3348..e95c8a18873 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -302,7 +302,7 @@ function drupal_execute($form_id, &$form_state) { // Make sure $form_state is passed around by reference. $args[1] = &$form_state; - + $form = call_user_func_array('drupal_retrieve_form', $args); $form['#post'] = $form_state['values']; @@ -773,8 +773,8 @@ function form_execute_handlers($type, &$form, &$form_state) { foreach ($handlers as $function) { if (function_exists($function)) { - // Check to see if a previous _submit handler has set a batch, but - // make sure we do not react to a batch that is already being processed + // Check to see if a previous _submit handler has set a batch, but + // make sure we do not react to a batch that is already being processed // (for instance if a batch operation performs a drupal_execute()). if ($type == 'submit' && ($batch =& batch_get()) && !isset($batch['current_set'])) { // Some previous _submit handler has set a batch. We store the call @@ -2425,8 +2425,8 @@ function form_clean_id($id = NULL, $flush = FALSE) { * - 'init_message': Message displayed while the processing is initialized. * Defaults to t('Initializing.'). * - 'progress_message': Message displayed while processing the batch. - * Available placeholders are @current, @remaining, @total, @percentage, - * @estimate and @elapsed. Defaults to t('Completed @current of @total.'). + * Available placeholders are @current, @remaining, @total, and + * @percentage. Defaults to t('Completed @current of @total.'). * - 'error_message': Message displayed if an error occurred while processing * the batch. Defaults to t('An error has occurred.'). * - 'finished': Name of a function to be executed after the batch has From c516e6b20337db6ed874a20d90db535f2de2fd35 Mon Sep 17 00:00:00 2001 From: Jennifer Hodgdon Date: Thu, 10 Jan 2013 15:38:25 -0800 Subject: [PATCH 02/14] Issue #1629994 by oadaeh, Aron Novak, yurtboy: Reference correct RFC in mail functions documentation --- includes/common.inc | 2 +- includes/mail.inc | 52 +++++++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/includes/common.inc b/includes/common.inc index ea353d19255..b7d671ca969 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -991,7 +991,7 @@ function t($string, $args = array(), $langcode = NULL) { /** * Verifies the syntax of the given e-mail address. * - * See RFC 2822 for details. + * See @link http://tools.ietf.org/html/rfc5322 RFC 5322 @endlink for details. * * @param $mail * A string containing an e-mail address. diff --git a/includes/mail.inc b/includes/mail.inc index a778ac551c8..d0515aca218 100644 --- a/includes/mail.inc +++ b/includes/mail.inc @@ -59,11 +59,13 @@ * will be {$module}_{$key}. * @param $to * The e-mail address or addresses where the message will be sent to. The - * formatting of this string must comply with RFC 2822. Some examples are: - * user@example.com - * user@example.com, anotheruser@example.com - * User - * User , Another User + * formatting of this string must comply with + * @link http://tools.ietf.org/html/rfc5322 RFC 5322 @endlink. + * Some examples are: + * - user@example.com + * - user@example.com, anotheruser@example.com + * - User + * - User , Another User * @param $language * Language object to use to compose the e-mail. * @param $params @@ -72,6 +74,7 @@ * Sets From to this value, if given. * @param $send * Send the message directly, without calling drupal_mail_send() manually. + * * @return * The $message array structure containing all details of the * message. If already sent ($send = TRUE), then the 'result' element @@ -145,26 +148,24 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N * how $message is composed. * * @param $message - * Message array with at least the following elements: - * - id - * A unique identifier of the e-mail type. Examples: 'contact_user_copy', - * 'user_password_reset'. - * - to - * The mail address or addresses where the message will be sent to. The - * formatting of this string must comply with RFC 2822. Some examples are: - * user@example.com - * user@example.com, anotheruser@example.com - * User - * User , Another User - * - subject - * Subject of the e-mail to be sent. This must not contain any newline - * characters, or the mail may not be sent properly. - * - body - * Message to be sent. Accepts both CRLF and LF line-endings. - * E-mail bodies must be wrapped. You can use drupal_wrap_mail() for - * smart plain text wrapping. - * - headers - * Associative array containing all mail headers. + * Message array with at least the following elements: + * - id: A unique identifier of the e-mail type. Examples: + * 'contact_user_copy', 'user_password_reset'. + * - to: The mail address or addresses where the message will be sent to. The + * formatting of this string must comply with + * @link http://tools.ietf.org/html/rfc5322 RFC 5322 @endlink. + * Some examples are: + * - user@example.com + * - user@example.com, anotheruser@example.com + * - User + * - User , Another User + * - subject: Subject of the e-mail to be sent. This must not contain any + * newline characters, or the mail may not be sent properly. + * - body: Message to be sent. Accepts both CRLF and LF line-endings. + * E-mail bodies must be wrapped. You can use drupal_wrap_mail() for + * smart plain text wrapping. + * - headers: Associative array containing all mail headers. + * * @return * Returns TRUE if the mail was successfully accepted for delivery, * FALSE otherwise. @@ -254,6 +255,7 @@ function drupal_wrap_mail($text, $indent = '') { * @param $allowed_tags (optional) * If supplied, a list of tags that will be transformed. If omitted, all * all supported tags are transformed. + * * @return * The transformed string. */ From 49d0eeaa7268ef57e98aefc7c0346fd7f2f22f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=CC=81bor=20Hojtsy?= Date: Wed, 16 Jan 2013 22:16:05 +0100 Subject: [PATCH 03/14] Now onto Drupal 6.29-dev --- CHANGELOG.txt | 3 +++ includes/common.inc | 2 +- modules/system/system.module | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index fa1fd027d2b..5c7df4b3d50 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,7 @@ +Drupal 6.29-dev, xxxx-xx-xx (development release) +---------------------- + Drupal 6.28, 2013-01-16 ---------------------- - Fixed security issues (multiple vulnerabilities), see SA-CORE-2013-001. diff --git a/includes/common.inc b/includes/common.inc index 5daec473746..b7d671ca969 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -665,7 +665,7 @@ function drupal_error_handler($errno, $message, $filename, $line, $context) { return; } - if ($errno & (E_ALL ^ E_DEPRECATED ^ E_NOTICE)) { + if ($errno & (E_ALL ^ E_DEPRECATED)) { $types = array(1 => 'error', 2 => 'warning', 4 => 'parse error', 8 => 'notice', 16 => 'core error', 32 => 'core warning', 64 => 'compile error', 128 => 'compile warning', 256 => 'user error', 512 => 'user warning', 1024 => 'user notice', 2048 => 'strict warning', 4096 => 'recoverable fatal error'); // For database errors, we want the line number/file name of the place that diff --git a/modules/system/system.module b/modules/system/system.module index 320f51fa157..45a829f2131 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.28'); +define('VERSION', '6.29-dev'); /** * Core API compatibility. From 6d92fa20927af334c990fd8a64850160504e0a74 Mon Sep 17 00:00:00 2001 From: Jennifer Hodgdon Date: Thu, 12 Sep 2013 07:38:12 -0700 Subject: [PATCH 04/14] Issue @2062127 by rhm50, jlindsey15, joachim: Fix documentation of system_requirements() to say it is a hook implementation --- modules/system/system.install | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/system/system.install b/modules/system/system.install index 2421dcb90ed..dfc395ac178 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -1,12 +1,7 @@ Date: Wed, 15 Jan 2014 20:35:48 +0100 Subject: [PATCH 05/14] Drupal 6.30 fixes --- CHANGELOG.txt | 3 +- includes/common.inc | 2 +- modules/openid/openid.install | 72 +++++++++++++++++++++++++++++++++-- modules/openid/openid.module | 2 +- modules/system/system.module | 2 +- 5 files changed, 74 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 7c7926fc064..b36af69f3e6 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,7 @@ -Drupal 6.30-dev, xxxx-xx-xx (development release) +Drupal 6.30, 2014-01-15 ---------------------- +- Fixed security issues (multiple vulnerabilities), see SA-CORE-2014-001. Drupal 6.29, 2013-11-20 ---------------------- diff --git a/includes/common.inc b/includes/common.inc index 61a3bac8a24..80fc9110f49 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -665,7 +665,7 @@ function drupal_error_handler($errno, $message, $filename, $line, $context) { return; } - if ($errno & (E_ALL ^ E_DEPRECATED)) { + if ($errno & (E_ALL ^ E_DEPRECATED ^ E_NOTICE)) { $types = array(1 => 'error', 2 => 'warning', 4 => 'parse error', 8 => 'notice', 16 => 'core error', 32 => 'core warning', 64 => 'compile error', 128 => 'compile warning', 256 => 'user error', 512 => 'user warning', 1024 => 'user notice', 2048 => 'strict warning', 4096 => 'recoverable fatal error'); // For database errors, we want the line number/file name of the place that diff --git a/modules/openid/openid.install b/modules/openid/openid.install index 0abc24f6f29..0b0c95d293e 100644 --- a/modules/openid/openid.install +++ b/modules/openid/openid.install @@ -26,13 +26,14 @@ function openid_schema() { 'idp_endpoint_uri' => array( 'type' => 'varchar', 'length' => 255, - 'description' => 'URI of the OpenID Provider endpoint.', + 'not null' => TRUE, + 'description' => 'Primary Key: URI of the OpenID Provider endpoint.', ), 'assoc_handle' => array( 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, - 'description' => 'Primary Key: Used to refer to this association in subsequent messages.', + 'description' => 'Used to refer to this association in subsequent messages.', ), 'assoc_type' => array( 'type' => 'varchar', @@ -62,7 +63,10 @@ function openid_schema() { 'description' => 'The lifetime, in seconds, of this association.', ), ), - 'primary key' => array('assoc_handle'), + 'primary key' => array('idp_endpoint_uri'), + 'unique keys' => array( + 'assoc_handle' => array('assoc_handle'), + ), ); $schema['openid_nonce'] = array( @@ -138,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/modules/openid/openid.module b/modules/openid/openid.module index 809e07e437d..4a7c57b9b89 100644 --- a/modules/openid/openid.module +++ b/modules/openid/openid.module @@ -551,7 +551,7 @@ function openid_verify_assertion($service, $response) { // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4 // Verify the signatures. $valid = FALSE; - $association = db_fetch_object(db_query("SELECT * FROM {openid_association} WHERE assoc_handle = '%s'", $response['openid.assoc_handle'])); + $association = db_fetch_object(db_query("SELECT * FROM {openid_association} WHERE idp_endpoint_uri = '%s' AND assoc_handle = '%s'", $service['uri'], $response['openid.assoc_handle'])); if ($association && isset($association->session_type)) { // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.2 // Verification using an association. diff --git a/modules/system/system.module b/modules/system/system.module index 6ef4699a7b0..09b9dde79af 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.30-dev'); +define('VERSION', '6.30'); /** * Core API compatibility. From c0ebecd8f4e24733635c1d04bc7e64868fbf3418 Mon Sep 17 00:00:00 2001 From: Jason Purdy Date: Wed, 15 Jan 2014 16:08:51 -0500 Subject: [PATCH 06/14] Drupal 6.30 --- CHANGELOG.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index ddfdf2c52fb..5bbf1e19e55 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,6 @@ +Drupal 6.30, 2014-01-15 +---------------------- +- Fixed security issues (multiple vulnerabilities), see SA-CORE-2014-001. Drupal 6.29, 2013-11-20 ---------------------- From b8184d7e3395434aad9c51e1e26415c55734b350 Mon Sep 17 00:00:00 2001 From: Jason Purdy Date: Wed, 15 Jan 2014 16:15:24 -0500 Subject: [PATCH 07/14] Merging Drupal 6.30 new schema and update function to put it into place. --- modules/openid/openid.install | 73 +++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/modules/openid/openid.install b/modules/openid/openid.install index 0abc24f6f29..e8b095f65d6 100644 --- a/modules/openid/openid.install +++ b/modules/openid/openid.install @@ -26,13 +26,14 @@ function openid_schema() { 'idp_endpoint_uri' => array( 'type' => 'varchar', 'length' => 255, - 'description' => 'URI of the OpenID Provider endpoint.', + 'not null' => TRUE, + 'description' => 'Primary Key: URI of the OpenID Provider endpoint.', ), 'assoc_handle' => array( 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, - 'description' => 'Primary Key: Used to refer to this association in subsequent messages.', + 'description' => 'Used to refer to this association in subsequent messages.', ), 'assoc_type' => array( 'type' => 'varchar', @@ -62,7 +63,10 @@ function openid_schema() { 'description' => 'The lifetime, in seconds, of this association.', ), ), - 'primary key' => array('assoc_handle'), + 'primary key' => array('idp_endpoint_uri'), + 'unique keys' => array( + 'assoc_handle' => array('assoc_handle'), + ), ); $schema['openid_nonce'] = array( @@ -138,6 +142,69 @@ 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. From 642334c0bd8e5b170a31c14bd47c5a04014f51f7 Mon Sep 17 00:00:00 2001 From: Jason Purdy Date: Wed, 15 Jan 2014 16:16:48 -0500 Subject: [PATCH 08/14] Merging Drupal 6.30 new sql call to get openid association --- modules/openid/openid.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openid/openid.module b/modules/openid/openid.module index 88c4df6b7cc..2271464e81c 100644 --- a/modules/openid/openid.module +++ b/modules/openid/openid.module @@ -553,7 +553,7 @@ function openid_verify_assertion($service, $response) { // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4 // Verify the signatures. $valid = FALSE; - $association = db_fetch_object(db_query("SELECT * FROM {openid_association} WHERE assoc_handle = '%s'", $response['openid.assoc_handle'])); + $association = db_fetch_object(db_query("SELECT * FROM {openid_association} WHERE idp_endpoint_uri = '%s' AND assoc_handle = '%s'", $service['uri'], $response['openid.assoc_handle'])); if ($association && isset($association->session_type)) { // http://openid.net/specs/openid-authentication-2_0.html#rfc.section.11.4.2 // Verification using an association. From 227a8ba2a422044be4928ae3d8afd425a94f984d Mon Sep 17 00:00:00 2001 From: Jason Purdy Date: Wed, 15 Jan 2014 16:17:37 -0500 Subject: [PATCH 09/14] Merging Drupal 6.30 updated version # to 6.30 --- modules/system/system.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/system/system.module b/modules/system/system.module index f1fa02283a6..8ee95478343 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.29'); +define('VERSION', '6.30'); /** * Core API compatibility. From 66e94d74994fced9fafbb2583f1c9e1bc636c04f Mon Sep 17 00:00:00 2001 From: David Rothstein Date: Wed, 16 Apr 2014 17:54:26 -0400 Subject: [PATCH 10/14] Drupal 6.31 --- CHANGELOG.txt | 4 ++++ includes/form.inc | 46 ++++++++++++++++++++++++++++++++++++ misc/ahah.js | 42 ++++++++++++++++++++++++++++++++ modules/system/system.module | 2 +- 4 files changed, 93 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index b36af69f3e6..a858e064b0f 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,8 @@ +Drupal 6.31, 2014-04-16 +---------------------- +- Fixed security issues (information disclosure). See SA-CORE-2014-002. + Drupal 6.30, 2014-01-15 ---------------------- - Fixed security issues (multiple vulnerabilities), see SA-CORE-2014-001. diff --git a/includes/form.inc b/includes/form.inc index 466410e4ab9..8ac40c95dc2 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -226,10 +226,25 @@ function form_set_cache($form_build_id, $form, $form_state) { if ($user->uid) { $form['#cache_token'] = drupal_get_token(); } + elseif (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED && $_SERVER['REQUEST_METHOD'] == 'GET' && page_get_cache(TRUE)) { + $form['#immutable'] = TRUE; + } + $form_build_id_old = $form_build_id; + $form_build_id = form_build_id_map($form_build_id_old); cache_set('form_'. $form_build_id, $form, 'cache_form', time() + $expire); if (!empty($form_state['storage'])) { cache_set('storage_'. $form_build_id, $form_state['storage'], 'cache_form', time() + $expire); } + + // If form_set_cache is called in the context of an ahah handler inform the + // client about the changed form build_id via the X-Drupal-Build-Id HTTP + // header. + if (!empty($_SERVER['HTTP_X_DRUPAL_ACCEPT_BUILD_ID']) && + !empty($_POST['form_build_id']) && + $_POST['form_build_id'] == $form_build_id_old && + $form_build_id_old != $form_build_id) { + drupal_set_header('X-Drupal-Build-Id: ' . $form_build_id); + } } /** @@ -243,11 +258,35 @@ function form_get_cache($form_build_id, &$form_state) { if ($cached = cache_get('storage_'. $form_build_id, 'cache_form')) { $form_state['storage'] = $cached->data; } + + // Generate a new #build_id if the cached form was rendered on a cacheable + // page. + if (!empty($form['#immutable'])) { + $form['#build_id'] = 'form-' . drupal_random_key(); + $form['form_build_id']['#value'] = $form['#build_id']; + $form['form_build_id']['#id'] = $form['#build_id']; + unset($form['#immutable']); + + form_build_id_map($form_build_id, $form['#build_id']); + } return $form; } } } +/** + * Maintain a map of immutable form_build_ids to cloned form. + */ +function form_build_id_map($form_build_id, $new_build_id = NULL) { + static $build_id_map = array(); + + if (isset($new_build_id) && isset($form_build_id)) { + $build_id_map[$form_build_id] = $new_build_id; + } + + return isset($build_id_map[$form_build_id]) ? $build_id_map[$form_build_id] : $form_build_id; +} + /** * Retrieves, populates, and processes a form. * @@ -1798,6 +1837,8 @@ function expand_radios($element) { * drupal_add_js. */ function form_expand_ahah($element) { + global $user; + static $js_added = array(); // Add a reasonable default event handler if none specified. if (isset($element['#ahah']['path']) && !isset($element['#ahah']['event'])) { @@ -1844,6 +1885,11 @@ function form_expand_ahah($element) { 'button' => isset($element['#executes_submit_callback']) ? array($element['#name'] => $element['#value']) : FALSE, ); + // If page caching is active, indicate that this form is immutable. + if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED && !$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && page_get_cache(TRUE)) { + $ahah_binding['immutable'] = TRUE; + } + // Convert a simple #ahah[progress] type string into an array. if (is_string($ahah_binding['progress'])) { $ahah_binding['progress'] = array('type' => $ahah_binding['progress']); diff --git a/misc/ahah.js b/misc/ahah.js index 118c4def4d2..e2a8659f539 100644 --- a/misc/ahah.js +++ b/misc/ahah.js @@ -44,6 +44,8 @@ Drupal.ahah = function(base, element_settings) { this.method = element_settings.method; this.progress = element_settings.progress; this.button = element_settings.button || { }; + this.immutable = element_settings.immutable; + this.buildId = null; if (this.effect == 'none') { this.showEffect = 'show'; @@ -76,6 +78,9 @@ Drupal.ahah = function(base, element_settings) { beforeSubmit: function(form_values, element_settings, options) { return ahah.beforeSubmit(form_values, element_settings, options); }, + beforeSend: function(request, options) { + return ahah.beforeSend(request, options); + }, success: function(response, status) { // Sanity check for browser support (object expected). // When using iFrame uploads, responses must be returned as a string. @@ -85,6 +90,7 @@ Drupal.ahah = function(base, element_settings) { return ahah.success(response, status); }, complete: function(response, status) { + ahah.complete(response, status); if (status == 'error' || status == 'parsererror') { return ahah.error(response, ahah.url); } @@ -139,8 +145,28 @@ Drupal.ahah.prototype.beforeSubmit = function (form_values, element, options) { } $(this.element).after(this.progress.element); } + + // Record the build-id. + if (this.immutable) { + var ahah = this; + $.each(form_values, function () { + if (this.name == 'form_build_id') { + ahah.buildId = this.value; + return false; + } + }); + } }; +/** + * Modify the request object before it is sent. + */ +Drupal.ahah.prototype.beforeSend = function (request, options) { + if (this.immutable) { + request.setRequestHeader('X-Drupal-Accept-Build-Id', '1'); + } +} + /** * Handler for the form redirection completion. */ @@ -222,3 +248,19 @@ Drupal.ahah.prototype.error = function (response, uri) { // Re-enable the element. $(this.element).removeClass('progess-disabled').attr('disabled', false); }; + +/** + * Handler called when the request finishes, whether in failure or success. + */ +Drupal.ahah.prototype.complete = function (response, status) { + // Update form build id if necessary. + if (this.immutable) { + var newBuildId = response.getResponseHeader('X-Drupal-Build-Id'); + if (this.buildId && newBuildId && this.buildId != newBuildId) { + var $element = $('input[name="form_build_id"][value="' + this.buildId + '"]'); + $element.val(newBuildId); + $element.attr('id', newBuildId); + } + this.buildId = null; + } +} diff --git a/modules/system/system.module b/modules/system/system.module index 09b9dde79af..221589f6cbf 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.30'); +define('VERSION', '6.31'); /** * Core API compatibility. From ef32540b17d87c490c8ae4b8769898796c1bcd9b Mon Sep 17 00:00:00 2001 From: David Rothstein Date: Wed, 16 Apr 2014 17:54:26 -0400 Subject: [PATCH 11/14] Drupal 6.31 via cherry-pick fixes #82 --- CHANGELOG.txt | 5 ++++ includes/form.inc | 46 ++++++++++++++++++++++++++++++++++++ misc/ahah.js | 42 ++++++++++++++++++++++++++++++++ modules/system/system.module | 2 +- 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 5bbf1e19e55..a858e064b0f 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,8 @@ + +Drupal 6.31, 2014-04-16 +---------------------- +- Fixed security issues (information disclosure). See SA-CORE-2014-002. + Drupal 6.30, 2014-01-15 ---------------------- - Fixed security issues (multiple vulnerabilities), see SA-CORE-2014-001. diff --git a/includes/form.inc b/includes/form.inc index 8d8c2af860a..94482d8a797 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -226,10 +226,25 @@ function form_set_cache($form_build_id, $form, $form_state) { if ($user->uid) { $form['#cache_token'] = drupal_get_token(); } + elseif (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED && $_SERVER['REQUEST_METHOD'] == 'GET' && page_get_cache(TRUE)) { + $form['#immutable'] = TRUE; + } + $form_build_id_old = $form_build_id; + $form_build_id = form_build_id_map($form_build_id_old); cache_set('form_'. $form_build_id, $form, 'cache_form', time() + $expire); if (!empty($form_state['storage'])) { cache_set('storage_'. $form_build_id, $form_state['storage'], 'cache_form', time() + $expire); } + + // If form_set_cache is called in the context of an ahah handler inform the + // client about the changed form build_id via the X-Drupal-Build-Id HTTP + // header. + if (!empty($_SERVER['HTTP_X_DRUPAL_ACCEPT_BUILD_ID']) && + !empty($_POST['form_build_id']) && + $_POST['form_build_id'] == $form_build_id_old && + $form_build_id_old != $form_build_id) { + drupal_set_header('X-Drupal-Build-Id: ' . $form_build_id); + } } /** @@ -243,11 +258,35 @@ function form_get_cache($form_build_id, &$form_state) { if ($cached = cache_get('storage_'. $form_build_id, 'cache_form')) { $form_state['storage'] = $cached->data; } + + // Generate a new #build_id if the cached form was rendered on a cacheable + // page. + if (!empty($form['#immutable'])) { + $form['#build_id'] = 'form-' . drupal_random_key(); + $form['form_build_id']['#value'] = $form['#build_id']; + $form['form_build_id']['#id'] = $form['#build_id']; + unset($form['#immutable']); + + form_build_id_map($form_build_id, $form['#build_id']); + } return $form; } } } +/** + * Maintain a map of immutable form_build_ids to cloned form. + */ +function form_build_id_map($form_build_id, $new_build_id = NULL) { + static $build_id_map = array(); + + if (isset($new_build_id) && isset($form_build_id)) { + $build_id_map[$form_build_id] = $new_build_id; + } + + return isset($build_id_map[$form_build_id]) ? $build_id_map[$form_build_id] : $form_build_id; +} + /** * Retrieves, populates, and processes a form. * @@ -1798,6 +1837,8 @@ function expand_radios($element) { * drupal_add_js. */ function form_expand_ahah($element) { + global $user; + static $js_added = array(); // Add a reasonable default event handler if none specified. if (isset($element['#ahah']['path']) && !isset($element['#ahah']['event'])) { @@ -1844,6 +1885,11 @@ function form_expand_ahah($element) { 'button' => isset($element['#executes_submit_callback']) ? array($element['#name'] => $element['#value']) : FALSE, ); + // If page caching is active, indicate that this form is immutable. + if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED && !$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && page_get_cache(TRUE)) { + $ahah_binding['immutable'] = TRUE; + } + // Convert a simple #ahah[progress] type string into an array. if (is_string($ahah_binding['progress'])) { $ahah_binding['progress'] = array('type' => $ahah_binding['progress']); diff --git a/misc/ahah.js b/misc/ahah.js index 118c4def4d2..e2a8659f539 100644 --- a/misc/ahah.js +++ b/misc/ahah.js @@ -44,6 +44,8 @@ Drupal.ahah = function(base, element_settings) { this.method = element_settings.method; this.progress = element_settings.progress; this.button = element_settings.button || { }; + this.immutable = element_settings.immutable; + this.buildId = null; if (this.effect == 'none') { this.showEffect = 'show'; @@ -76,6 +78,9 @@ Drupal.ahah = function(base, element_settings) { beforeSubmit: function(form_values, element_settings, options) { return ahah.beforeSubmit(form_values, element_settings, options); }, + beforeSend: function(request, options) { + return ahah.beforeSend(request, options); + }, success: function(response, status) { // Sanity check for browser support (object expected). // When using iFrame uploads, responses must be returned as a string. @@ -85,6 +90,7 @@ Drupal.ahah = function(base, element_settings) { return ahah.success(response, status); }, complete: function(response, status) { + ahah.complete(response, status); if (status == 'error' || status == 'parsererror') { return ahah.error(response, ahah.url); } @@ -139,8 +145,28 @@ Drupal.ahah.prototype.beforeSubmit = function (form_values, element, options) { } $(this.element).after(this.progress.element); } + + // Record the build-id. + if (this.immutable) { + var ahah = this; + $.each(form_values, function () { + if (this.name == 'form_build_id') { + ahah.buildId = this.value; + return false; + } + }); + } }; +/** + * Modify the request object before it is sent. + */ +Drupal.ahah.prototype.beforeSend = function (request, options) { + if (this.immutable) { + request.setRequestHeader('X-Drupal-Accept-Build-Id', '1'); + } +} + /** * Handler for the form redirection completion. */ @@ -222,3 +248,19 @@ Drupal.ahah.prototype.error = function (response, uri) { // Re-enable the element. $(this.element).removeClass('progess-disabled').attr('disabled', false); }; + +/** + * Handler called when the request finishes, whether in failure or success. + */ +Drupal.ahah.prototype.complete = function (response, status) { + // Update form build id if necessary. + if (this.immutable) { + var newBuildId = response.getResponseHeader('X-Drupal-Build-Id'); + if (this.buildId && newBuildId && this.buildId != newBuildId) { + var $element = $('input[name="form_build_id"][value="' + this.buildId + '"]'); + $element.val(newBuildId); + $element.attr('id', newBuildId); + } + this.buildId = null; + } +} diff --git a/modules/system/system.module b/modules/system/system.module index 8ee95478343..b84c280efcf 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.30'); +define('VERSION', '6.31'); /** * Core API compatibility. From d5ac1441acd1e6ed1d3309ebcc6cd51cb39c6a84 Mon Sep 17 00:00:00 2001 From: pwolanin Date: Thu, 17 Apr 2014 09:42:20 -0400 Subject: [PATCH 12/14] Fix #84 by using drupal_page_is_cacheable() --- includes/form.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/form.inc b/includes/form.inc index 94482d8a797..612fd3eca83 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -226,7 +226,7 @@ function form_set_cache($form_build_id, $form, $form_state) { if ($user->uid) { $form['#cache_token'] = drupal_get_token(); } - elseif (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED && $_SERVER['REQUEST_METHOD'] == 'GET' && page_get_cache(TRUE)) { + elseif (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED && drupal_page_is_cacheable()) { $form['#immutable'] = TRUE; } $form_build_id_old = $form_build_id; @@ -1886,7 +1886,7 @@ function form_expand_ahah($element) { ); // If page caching is active, indicate that this form is immutable. - if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED && !$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && page_get_cache(TRUE)) { + if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED && !$user->uid && drupal_page_is_cacheable()) { $ahah_binding['immutable'] = TRUE; } From 92eedf2c17bcea6db47c6b317c6ebf6078bfffae Mon Sep 17 00:00:00 2001 From: David Rothstein Date: Wed, 16 Jul 2014 16:27:22 -0400 Subject: [PATCH 13/14] Drupal 6.32 --- CHANGELOG.txt | 4 +++ includes/bootstrap.inc | 9 +++++- includes/file.inc | 59 +++++++++++++++++++++++++++++++++--- includes/form.inc | 2 +- modules/system/system.module | 2 +- 5 files changed, 69 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index a858e064b0f..2b8d4af1335 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,8 @@ +Drupal 6.32, 2014-07-16 +---------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-003. + Drupal 6.31, 2014-04-16 ---------------------- - Fixed security issues (information disclosure). See SA-CORE-2014-002. diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index d6b407ced26..c8d67393174 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -364,7 +364,14 @@ function drupal_unset_globals() { * TRUE if only containing valid characters, or FALSE otherwise. */ function drupal_valid_http_host($host) { - return preg_match('/^\[?(?:[a-z0-9-:\]_]+\.?)+$/', $host); + // Limit the length of the host name to 1000 bytes to prevent DoS attacks with + // long host names. + return strlen($host) <= 1000 + // Limit the number of subdomains and port separators to prevent DoS attacks + // in conf_path(). + && substr_count($host, '.') <= 100 + && substr_count($host, ':') <= 100 + && preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host); } /** diff --git a/includes/file.inc b/includes/file.inc index d0e24b2e98b..e606ba2a61d 100644 --- a/includes/file.inc +++ b/includes/file.inc @@ -974,17 +974,68 @@ function file_download() { } if (file_exists(file_create_path($filepath))) { - $headers = module_invoke_all('file_download', $filepath); - if (in_array(-1, $headers)) { - return drupal_access_denied(); - } + $headers = file_download_headers($filepath); if (count($headers)) { file_transfer($filepath, $headers); } + else { + return drupal_access_denied(); + } } return drupal_not_found(); } +/** + * Retrieves headers for a private file download. + * + * Calls all module implementations of hook_file_download() to retrieve headers + * for files by the module that originally provided the file. The presence of + * returned headers indicates the current user has access to the file. + * + * @param $filepath + * The path for the file whose headers should be retrieved. + * + * @return + * If access is allowed, headers for the file, suitable for passing to + * file_transfer(). If access is not allowed, an empty array will be returned. + * + * @see file_transfer() + * @see file_download_access() + * @see hook_file_downlaod() + */ +function file_download_headers($filepath) { + $headers = module_invoke_all('file_download', $filepath); + if (in_array(-1, $headers)) { + // Throw away the headers received so far. + $headers = array(); + } + return $headers; +} + +/** + * Checks that the current user has access to a particular file. + * + * The return value of this function hinges on the return value from + * file_download_headers(), which is the function responsible for collecting + * access information through hook_file_download(). + * + * If immediately transferring the file to the browser and the headers will + * need to be retrieved, the return value of file_download_headers() should be + * used to determine access directly, so that access checks will not be run + * twice. + * + * @param $filepath + * The path for the file whose headers should be retrieved. + * + * @return + * Boolean TRUE if access is allowed. FALSE if access is not allowed. + * + * @see file_download_headers() + * @see hook_file_download() + */ +function file_download_access($filepath) { + return count(file_download_headers($filepath)) > 0; +} /** * Finds all files that match a given mask in a given directory. diff --git a/includes/form.inc b/includes/form.inc index 8ac40c95dc2..e9ac8e40fe8 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -1484,7 +1484,7 @@ function form_select_options($element, $choices = NULL) { $options = ''; foreach ($choices as $key => $choice) { if (is_array($choice)) { - $options .= ''; + $options .= ''; $options .= form_select_options($element, $choice); $options .= ''; } diff --git a/modules/system/system.module b/modules/system/system.module index 221589f6cbf..8312b1d0a33 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.31'); +define('VERSION', '6.32'); /** * Core API compatibility. From c71b15f68010db028f07839c226d31563f220890 Mon Sep 17 00:00:00 2001 From: David Rothstein Date: Wed, 6 Aug 2014 13:07:34 -0400 Subject: [PATCH 14/14] Drupal 6.33 --- CHANGELOG.txt | 4 ++++ includes/xmlrpc.inc | 33 ++++++++++++++++++++++++++++++++- modules/openid/xrds.inc | 16 ++++++++++++++++ modules/system/system.module | 2 +- 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 2b8d4af1335..814adba801b 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,8 @@ +Drupal 6.33, 2014-08-06 +---------------------- +- Fixed security issues (denial of service). See SA-CORE-2014-004. + Drupal 6.32, 2014-07-16 ---------------------- - Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-003. diff --git a/includes/xmlrpc.inc b/includes/xmlrpc.inc index 13ebf09ffab..9236d881a31 100644 --- a/includes/xmlrpc.inc +++ b/includes/xmlrpc.inc @@ -163,7 +163,38 @@ function xmlrpc_message_parse(&$xmlrpc_message) { xml_set_element_handler($xmlrpc_message->_parser, 'xmlrpc_message_tag_open', 'xmlrpc_message_tag_close'); xml_set_character_data_handler($xmlrpc_message->_parser, 'xmlrpc_message_cdata'); xmlrpc_message_set($xmlrpc_message); - if (!xml_parse($xmlrpc_message->_parser, $xmlrpc_message->message)) { + + // Strip XML declaration. + $header = preg_replace('/<\?xml.*?\?'.'>/s', '', substr($xmlrpc_message->message, 0, 100), 1); + $xml = trim(substr_replace($xmlrpc_message->message, $header, 0, 100)); + if ($xml == '') { + return FALSE; + } + // Strip DTD. + $header = preg_replace('/^]*+>/i', '', substr($xml, 0, 200), 1); + $xml = trim(substr_replace($xml, $header, 0, 200)); + if ($xml == '') { + return FALSE; + } + // Confirm the XML now starts with a valid root tag. A root tag can end in [> \t\r\n] + $root_tag = substr($xml, 0, strcspn(substr($xml, 0, 20), "> \t\r\n")); + // Reject a second DTD. + if (strtoupper($root_tag) == ' 2 * variable_get('xmlrpc_message_maximum_tag_count', 30000)) { + return FALSE; + } + + if (!xml_parse($xmlrpc_message->_parser, $xml)) { return FALSE; } xml_parser_free($xmlrpc_message->_parser); diff --git a/modules/openid/xrds.inc b/modules/openid/xrds.inc index 36f5282fd13..7810b3c85f3 100644 --- a/modules/openid/xrds.inc +++ b/modules/openid/xrds.inc @@ -15,6 +15,22 @@ function xrds_parse($xml) { xml_set_element_handler($parser, '_xrds_element_start', '_xrds_element_end'); xml_set_character_data_handler($parser, '_xrds_cdata'); + // Since DOCTYPE declarations from an untrusted source could be malicious, we + // stop parsing here and treat the XML as invalid. XRDS documents do not + // require, and are not expected to have, a DOCTYPE. + if (preg_match('/ 2 * variable_get('openid_xrds_maximum_tag_count', 30000)) { + return array(); + } + xml_parse($parser, $xml); xml_parser_free($parser); diff --git a/modules/system/system.module b/modules/system/system.module index 8312b1d0a33..9e852c21fb3 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.32'); +define('VERSION', '6.33'); /** * Core API compatibility.