summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwebchick <webchick@24967.no-reply.drupal.org>2012-02-01 13:29:51 -0800
committerwebchick <webchick@24967.no-reply.drupal.org>2012-02-01 13:29:51 -0800
commit40093b2fa7dde4a5f3c6806aad91b9302c232903 (patch)
tree0c5ce465e4a9c25113c536d39f1051b65f403f68
parent2dd2f4f2a215e0c023513d5a63df0dd1205776fc (diff)
downloadbrdo-40093b2fa7dde4a5f3c6806aad91b9302c232903.tar.gz
brdo-40093b2fa7dde4a5f3c6806aad91b9302c232903.tar.bz2
SA-CORE-2012-001
-rw-r--r--CHANGELOG.txt23
-rw-r--r--includes/bootstrap.inc2
-rw-r--r--modules/aggregator/aggregator.admin.inc5
-rw-r--r--modules/aggregator/aggregator.test13
-rw-r--r--modules/file/file.api.php12
-rw-r--r--modules/file/file.module25
-rw-r--r--modules/file/tests/file.test15
-rw-r--r--modules/openid/openid.inc23
-rw-r--r--modules/openid/openid.module13
-rw-r--r--modules/openid/openid.test77
-rw-r--r--modules/openid/tests/openid_test.module23
11 files changed, 186 insertions, 45 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 413864640..76d7f8aec 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,7 +1,26 @@
-Drupal 7.10, xxxx-xx-xx (development version)
------------------------
+Drupal 7.11, 2012-02-01
+----------------------
+- Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-001.
+Drupal 7.10, 2011-12-05
+----------------------
+- Fixed Content-Language HTTP header to not cause issues with Drush 5.x.
+- Reduce memory usage of theme registry (performance).
+- Fixed PECL upload progress bar for FileField
+- Fixed running update.php doesn't always clear the cache.
+- Fixed PDO exceptions on long titles.
+- Fixed Overlay redirect does not include query string.
+- Fixed D6 modules satisfy D7 module dependencies.
+- Fixed the ordering of module hooks when using module_implements_alter().
+- Fixed "floating" submit buttons during AJAX requests.
+- Fixed timezone selected on install not propogating to admin account.
+- Added msgctx context to JS translation functions, for feature parity with t().
+- Profiles' .install files now available during hook_install_tasks().
+- Added test coverage of 7.0 -> 7.x upgrade path.
+- Numerous notice fixes.
+- Numerous documentation improvements.
+- Additional automated test coverage.
Drupal 7.9, 2011-10-26
----------------------
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index 026545794..292b8bb87 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -8,7 +8,7 @@
/**
* The current system version.
*/
-define('VERSION', '7.10-dev');
+define('VERSION', '7.11');
/**
* Core API compatibility.
diff --git a/modules/aggregator/aggregator.admin.inc b/modules/aggregator/aggregator.admin.inc
index 08087afb2..52af1a631 100644
--- a/modules/aggregator/aggregator.admin.inc
+++ b/modules/aggregator/aggregator.admin.inc
@@ -33,7 +33,7 @@ function aggregator_view() {
($feed->checked && $feed->refresh ? t('%time left', array('%time' => format_interval($feed->checked + $feed->refresh - REQUEST_TIME))) : t('never')),
l(t('edit'), "admin/config/services/aggregator/edit/feed/$feed->fid"),
l(t('remove items'), "admin/config/services/aggregator/remove/$feed->fid"),
- l(t('update items'), "admin/config/services/aggregator/update/$feed->fid"),
+ l(t('update items'), "admin/config/services/aggregator/update/$feed->fid", array('query' => array('token' => drupal_get_token("aggregator/update/$feed->fid")))),
);
}
$output .= theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No feeds available. <a href="@link">Add feed</a>.', array('@link' => url('admin/config/services/aggregator/add/feed')))));
@@ -386,6 +386,9 @@ function _aggregator_parse_opml($opml) {
* An object describing the feed to be refreshed.
*/
function aggregator_admin_refresh_feed($feed) {
+ if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], 'aggregator/update/' . $feed->fid)) {
+ return MENU_ACCESS_DENIED;
+ }
aggregator_refresh($feed);
drupal_goto('admin/config/services/aggregator');
}
diff --git a/modules/aggregator/aggregator.test b/modules/aggregator/aggregator.test
index 0d1e31ba5..fd3e852fc 100644
--- a/modules/aggregator/aggregator.test
+++ b/modules/aggregator/aggregator.test
@@ -92,8 +92,13 @@ class AggregatorTestCase extends DrupalWebTestCase {
$this->drupalGet($feed->url);
$this->assertResponse(200, t('!url is reachable.', array('!url' => $feed->url)));
- // Refresh the feed (simulated link click).
+ // Attempt to access the update link directly without an access token.
$this->drupalGet('admin/config/services/aggregator/update/' . $feed->fid);
+ $this->assertResponse(403);
+
+ // Refresh the feed (simulated link click).
+ $this->drupalGet('admin/config/services/aggregator');
+ $this->clickLink('update items');
// Ensure we have the right number of items.
$result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid));
@@ -466,8 +471,8 @@ class UpdateFeedItemTestCase extends AggregatorTestCase {
$this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title'])), t('The feed !name has been added.', array('!name' => $edit['title'])));
$feed = db_query("SELECT * FROM {aggregator_feed} WHERE url = :url", array(':url' => $edit['url']))->fetchObject();
- $this->drupalGet('admin/config/services/aggregator/update/' . $feed->fid);
+ aggregator_refresh($feed);
$before = db_query('SELECT timestamp FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField();
// Sleep for 3 second.
@@ -481,10 +486,9 @@ class UpdateFeedItemTestCase extends AggregatorTestCase {
'modified' => 0,
))
->execute();
- $this->drupalGet('admin/config/services/aggregator/update/' . $feed->fid);
+ aggregator_refresh($feed);
$after = db_query('SELECT timestamp FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField();
-
$this->assertTrue($before === $after, t('Publish timestamp of feed item was not updated (!before === !after)', array('!before' => $before, '!after' => $after)));
}
}
@@ -884,4 +888,3 @@ class FeedParserTestCase extends AggregatorTestCase {
$this->assertEqual('urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', db_query('SELECT guid FROM {aggregator_item} WHERE link = :link', array(':link' => 'http://example.org/2003/12/13/atom03'))->fetchField(), 'Atom entry id element is parsed correctly.');
}
}
-
diff --git a/modules/file/file.api.php b/modules/file/file.api.php
index 76fb98610..663682033 100644
--- a/modules/file/file.api.php
+++ b/modules/file/file.api.php
@@ -12,8 +12,8 @@
* file is referenced, e.g., only users with access to a node should be allowed
* to download files attached to that node.
*
- * @param $field
- * The field to which the file belongs.
+ * @param array $file_item
+ * The array of information about the file to check access for.
* @param $entity_type
* The type of $entity; for example, 'node' or 'user'.
* @param $entity
@@ -26,7 +26,7 @@
*
* @see hook_field_access().
*/
-function hook_file_download_access($field, $entity_type, $entity) {
+function hook_file_download_access($file_item, $entity_type, $entity) {
if ($entity_type == 'node') {
return node_access('view', $entity);
}
@@ -45,8 +45,8 @@ function hook_file_download_access($field, $entity_type, $entity) {
* An array of grants gathered by hook_file_download_access(). The array is
* keyed by the module that defines the entity type's access control; the
* values are Boolean grant responses for each module.
- * @param $field
- * The field to which the file belongs.
+ * @param array $file_item
+ * The array of information about the file to alter access for.
* @param $entity_type
* The type of $entity; for example, 'node' or 'user'.
* @param $entity
@@ -58,7 +58,7 @@ function hook_file_download_access($field, $entity_type, $entity) {
* module's value in addition to other grants or to overwrite the values set by
* other modules.
*/
-function hook_file_download_access_alter(&$grants, $field, $entity_type, $entity) {
+function hook_file_download_access_alter(&$grants, $file_item, $entity_type, $entity) {
// For our example module, we always enforce the rules set by node module.
if (isset($grants['node'])) {
$grants = array('node' => $grants['node']);
diff --git a/modules/file/file.module b/modules/file/file.module
index 5d7b17af5..83e960017 100644
--- a/modules/file/file.module
+++ b/modules/file/file.module
@@ -165,24 +165,27 @@ function file_file_download($uri, $field_type = 'file') {
// Try to load $entity and $field.
$entity = entity_load($entity_type, array($id));
$entity = reset($entity);
- $field = NULL;
+ $field = field_info_field($field_name);
+
+ // Load the field item that references the file.
+ $field_item = NULL;
if ($entity) {
- // Load all fields for that entity.
+ // Load all field items for that entity.
$field_items = field_get_items($entity_type, $entity, $field_name);
// Find the field item with the matching URI.
- foreach ($field_items as $field_item) {
- if ($field_item['uri'] == $uri) {
- $field = $field_item;
+ foreach ($field_items as $item) {
+ if ($item['uri'] == $uri) {
+ $field_item = $item;
break;
}
}
}
- // Check that $entity and $field were loaded successfully and check if
- // access to that field is not disallowed. If any of these checks fail,
- // stop checking access for this reference.
- if (empty($entity) || empty($field) || !field_access('view', $field, $entity_type, $entity)) {
+ // Check that $entity, $field and $field_item were loaded successfully
+ // and check if access to that field is not disallowed. If any of these
+ // checks fail, stop checking access for this reference.
+ if (empty($entity) || empty($field) || empty($field_item) || !field_access('view', $field, $entity_type, $entity)) {
$denied = TRUE;
break;
}
@@ -191,10 +194,10 @@ function file_file_download($uri, $field_type = 'file') {
// Default to FALSE and let entities overrule this ruling.
$grants = array('system' => FALSE);
foreach (module_implements('file_download_access') as $module) {
- $grants = array_merge($grants, array($module => module_invoke($module, 'file_download_access', $field, $entity_type, $entity)));
+ $grants = array_merge($grants, array($module => module_invoke($module, 'file_download_access', $field_item, $entity_type, $entity)));
}
// Allow other modules to alter the returned grants/denies.
- drupal_alter('file_download_access', $grants, $field, $entity_type, $entity);
+ drupal_alter('file_download_access', $grants, $field_item, $entity_type, $entity);
if (in_array(TRUE, $grants)) {
// If TRUE is returned, access is granted and no further checks are
diff --git a/modules/file/tests/file.test b/modules/file/tests/file.test
index 59f6e0cb0..324faffc4 100644
--- a/modules/file/tests/file.test
+++ b/modules/file/tests/file.test
@@ -1107,7 +1107,7 @@ class FilePrivateTestCase extends FileFieldTestCase {
}
function setUp() {
- parent::setUp('node_access_test');
+ parent::setUp(array('node_access_test', 'field_test'));
node_access_rebuild();
variable_set('node_access_test_private', TRUE);
}
@@ -1124,6 +1124,10 @@ class FilePrivateTestCase extends FileFieldTestCase {
$field_name = strtolower($this->randomName());
$this->createFileField($field_name, $type_name, array('uri_scheme' => 'private'));
+ // Create a field with no view access - see field_test_field_access().
+ $no_access_field_name = 'field_no_view_access';
+ $this->createFileField($no_access_field_name, $type_name, array('uri_scheme' => 'private'));
+
$test_file = $this->getTestFile('text');
$nid = $this->uploadNodeFile($test_file, $field_name, $type_name, TRUE, array('private' => TRUE));
$node = node_load($nid, NULL, TRUE);
@@ -1134,5 +1138,14 @@ class FilePrivateTestCase extends FileFieldTestCase {
$this->drupalLogOut();
$this->drupalGet(file_create_url($node_file->uri));
$this->assertResponse(403, t('Confirmed that access is denied for the file without the needed permission.'));
+
+ // Test with the field that should deny access through field access.
+ $this->drupalLogin($this->admin_user);
+ $nid = $this->uploadNodeFile($test_file, $no_access_field_name, $type_name, TRUE, array('private' => TRUE));
+ $node = node_load($nid, NULL, TRUE);
+ $node_file = (object) $node->{$no_access_field_name}[LANGUAGE_NONE][0];
+ // Ensure the file cannot be downloaded.
+ $this->drupalGet(file_create_url($node_file->uri));
+ $this->assertResponse(403, t('Confirmed that access is denied for the file without view field access permission.'));
}
}
diff --git a/modules/openid/openid.inc b/modules/openid/openid.inc
index 98af518c7..9b793d368 100644
--- a/modules/openid/openid.inc
+++ b/modules/openid/openid.inc
@@ -617,18 +617,31 @@ function _openid_get_params($str) {
* @param $fallback_prefix
* An optional prefix that will be used in case no prefix is found for the
* target extension namespace.
+ * @param $only_signed
+ * Return only keys that are included in the message signature in openid.sig.
+ * Unsigned fields may have been modified or added by other parties than the
+ * OpenID Provider.
+ *
* @return
* An associative array containing all the parameters in the response message
* that belong to the extension. The keys are stripped from their namespace
* prefix.
+ *
* @see http://openid.net/specs/openid-authentication-2_0.html#extensions
*/
-function openid_extract_namespace($response, $extension_namespace, $fallback_prefix = NULL) {
+function openid_extract_namespace($response, $extension_namespace, $fallback_prefix = NULL, $only_signed = FALSE) {
+ $signed_keys = explode(',', $response['openid.signed']);
+
// Find the namespace prefix.
$prefix = $fallback_prefix;
foreach ($response as $key => $value) {
if ($value == $extension_namespace && preg_match('/^openid\.ns\.([^.]+)$/', $key, $matches)) {
$prefix = $matches[1];
+ if ($only_signed && !in_array('ns.' . $matches[1], $signed_keys)) {
+ // The namespace was defined but was not signed as required. In this
+ // case we do not fall back to $fallback_prefix.
+ $prefix = NULL;
+ }
break;
}
}
@@ -641,7 +654,9 @@ function openid_extract_namespace($response, $extension_namespace, $fallback_pre
foreach ($response as $key => $value) {
if (preg_match('/^openid\.' . $prefix . '\.(.+)$/', $key, $matches)) {
$local_key = $matches[1];
- $output[$local_key] = $value;
+ if (!$only_signed || in_array($prefix . '.' . $local_key, $signed_keys)) {
+ $output[$local_key] = $value;
+ }
}
}
@@ -837,8 +852,8 @@ function _openid_invalid_openid_transition($identity, $response) {
// Try to extract e-mail address from Simple Registration (SREG) or
// Attribute Exchanges (AX) keys.
$email = '';
- $sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg');
- $ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax');
+ $sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE);
+ $ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax', TRUE);
if (!empty($sreg_values['email']) && valid_email_address($sreg_values['email'])) {
$email = $sreg_values['email'];
}
diff --git a/modules/openid/openid.module b/modules/openid/openid.module
index f2847fc0d..e08d55718 100644
--- a/modules/openid/openid.module
+++ b/modules/openid/openid.module
@@ -185,10 +185,15 @@ function openid_form_user_register_form_alter(&$form, &$form_state) {
$response = $_SESSION['openid']['response'];
- // Extract Simple Registration keys from the response.
- $sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg');
- // Extract Attribute Exchanges keys from the response.
- $ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax');
+ // Extract Simple Registration keys from the response. We only include
+ // signed keys as required by OpenID Simple Registration Extension 1.0,
+ // section 4.
+ $sreg_values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE);
+ // Extract Attribute Exchanges keys from the response. We only include
+ // signed keys. This is not required by the specification, but it is
+ // recommended by Google, see
+ // http://googlecode.blogspot.com/2011/05/security-advisory-to-websites-using.html
+ $ax_values = openid_extract_namespace($response, OPENID_NS_AX, 'ax', TRUE);
if (!empty($sreg_values['nickname'])) {
// Use the nickname returned by Simple Registration if available.
diff --git a/modules/openid/openid.test b/modules/openid/openid.test
index afb9068c6..9b6b1ad5f 100644
--- a/modules/openid/openid.test
+++ b/modules/openid/openid.test
@@ -343,17 +343,49 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase {
// Use a User-supplied Identity that is the URL of an XRDS document.
$identity = url('openid-test/yadis/xrds', array('absolute' => TRUE));
- // Do not sign all mandatory fields (e.g. assoc_handle).
+ // Respond with an invalid signature.
+ variable_set('openid_test_response', array('openid.sig' => 'this-is-an-invalid-signature'));
+ $this->submitLoginForm($identity);
+ $this->assertRaw('OpenID login failed.');
+
+ // Do not sign the mandatory field openid.assoc_handle.
variable_set('openid_test_response', array('openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce'));
$this->submitLoginForm($identity);
$this->assertRaw('OpenID login failed.');
- // Sign all mandatory fields and some custom fields.
- variable_set('openid_test_response', array('openid.foo' => 'bar', 'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,foo'));
+ // Sign all mandatory fields and a custom field.
+ $keys_to_sign = array('op_endpoint', 'claimed_id', 'identity', 'return_to', 'response_nonce', 'assoc_handle', 'foo');
+ $association = new stdClass();
+ $association->mac_key = variable_get('mac_key');
+ $response = array(
+ 'openid.op_endpoint' => url('openid-test/endpoint', array('absolute' => TRUE)),
+ 'openid.claimed_id' => $identity,
+ 'openid.identity' => $identity,
+ 'openid.return_to' => url('openid/authenticate', array('absolute' => TRUE)),
+ 'openid.response_nonce' => _openid_nonce(),
+ 'openid.assoc_handle' => 'openid-test',
+ 'openid.foo' => 123,
+ 'openid.signed' => implode(',', $keys_to_sign),
+ );
+ $response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign);
+ variable_set('openid_test_response', $response);
$this->submitLoginForm($identity);
$this->assertNoRaw('OpenID login failed.');
- }
+ $this->assertFieldByName('name', '', t('No username was supplied by provider.'));
+ $this->assertFieldByName('mail', '', t('No e-mail address was supplied by provider.'));
+ // Check that unsigned SREG fields are ignored.
+ $response = array(
+ 'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,sreg.nickname',
+ 'openid.sreg.nickname' => 'john',
+ 'openid.sreg.email' => 'john@example.com',
+ );
+ variable_set('openid_test_response', $response);
+ $this->submitLoginForm($identity);
+ $this->assertNoRaw('OpenID login failed.');
+ $this->assertFieldByName('name', 'john', t('Username was supplied by provider.'));
+ $this->assertFieldByName('mail', '', t('E-mail address supplied by provider was ignored.'));
+ }
}
/**
@@ -728,4 +760,41 @@ class OpenIDUnitTest extends DrupalWebTestCase {
$this->assertEqual(openid_normalize('http://example.com/path#fragment'), 'http://example.com/path', t('openid_normalize() correctly normalized a URL with a fragment.'));
}
+
+ /**
+ * Test openid_extract_namespace().
+ */
+ function testOpenidExtractNamespace() {
+ $response = array(
+ 'openid.sreg.nickname' => 'john',
+ 'openid.ns.ext1' => OPENID_NS_SREG,
+ 'openid.ext1.nickname' => 'george',
+ 'openid.ext1.email' => 'george@example.com',
+ 'openid.ns.ext2' => 'http://example.com/ns/ext2',
+ 'openid.ext2.foo' => '123',
+ 'openid.ext2.bar' => '456',
+ 'openid.signed' => 'sreg.nickname,ns.ext1,ext1.email,ext2.foo',
+ );
+
+ $values = openid_extract_namespace($response, 'http://example.com/ns/dummy', NULL, FALSE);
+ $this->assertEqual($values, array(), t('Nothing found for unused namespace.'));
+
+ $values = openid_extract_namespace($response, 'http://example.com/ns/dummy', 'sreg', FALSE);
+ $this->assertEqual($values, array('nickname' => 'john'), t('Value found for fallback prefix.'));
+
+ $values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', FALSE);
+ $this->assertEqual($values, array('nickname' => 'george', 'email' => 'george@example.com'), t('Namespace takes precedence over fallback prefix.'));
+
+ // ext1.email is signed, but ext1.nickname is not.
+ $values = openid_extract_namespace($response, OPENID_NS_SREG, 'sreg', TRUE);
+ $this->assertEqual($values, array('email' => 'george@example.com'), t('Unsigned namespaced fields ignored.'));
+
+ $values = openid_extract_namespace($response, 'http://example.com/ns/ext2', 'sreg', FALSE);
+ $this->assertEqual($values, array('foo' => '123', 'bar' => '456'), t('Unsigned fields found.'));
+
+ // ext2.foo and ext2.bar are ignored, because ns.ext2 is not signed. The
+ // fallback prefix is not used, because the namespace is specified.
+ $values = openid_extract_namespace($response, 'http://example.com/ns/ext2', 'sreg', TRUE);
+ $this->assertEqual($values, array(), t('Unsigned fields ignored.'));
+ }
}
diff --git a/modules/openid/tests/openid_test.module b/modules/openid/tests/openid_test.module
index 629dcd335..1b0de4ec5 100644
--- a/modules/openid/tests/openid_test.module
+++ b/modules/openid/tests/openid_test.module
@@ -324,9 +324,7 @@ function _openid_test_endpoint_authenticate() {
// Generate unique identifier for this authentication.
$nonce = _openid_nonce();
- // Generate response containing the user's identity. The openid.sreg.xxx
- // entries contain profile data stored by the OpenID Provider (see OpenID
- // Simple Registration Extension 1.0).
+ // Generate response containing the user's identity.
$response = variable_get('openid_test_response', array()) + array(
'openid.ns' => OPENID_NS_2_0,
'openid.mode' => 'id_res',
@@ -336,14 +334,27 @@ function _openid_test_endpoint_authenticate() {
'openid.return_to' => $_REQUEST['openid_return_to'],
'openid.response_nonce' => $nonce,
'openid.assoc_handle' => 'openid-test',
- 'openid.signed' => 'op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle',
);
+ if (isset($response['openid.signed'])) {
+ $keys_to_sign = explode(',', $response['openid.signed']);
+ }
+ else {
+ // Unless openid.signed is explicitly defined, all keys are signed.
+ $keys_to_sign = array();
+ foreach ($response as $key => $value) {
+ // Strip off the "openid." prefix.
+ $keys_to_sign[] = substr($key, 7);
+ }
+ $response['openid.signed'] = implode(',', $keys_to_sign);
+ }
+
// Sign the message using the MAC key that was exchanged during association.
$association = new stdClass();
$association->mac_key = variable_get('mac_key');
- $keys_to_sign = explode(',', $response['openid.signed']);
- $response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign);
+ if (!isset($response['openid.sig'])) {
+ $response['openid.sig'] = _openid_signature($association, $response, $keys_to_sign);
+ }
// Put the signed message into the query string of a URL supplied by the
// Relying Party, and redirect the user.