summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2009-07-22 04:45:35 +0000
committerDries Buytaert <dries@buytaert.net>2009-07-22 04:45:35 +0000
commit3a29ee48f82eed3adfd2a90d0eae63a5903149fa (patch)
treec0de7a472a7db4dff57dd9b89c25d5e084c79ca8 /includes
parent4ddecc05c9d2054568300d25748569dcd34fec7d (diff)
downloadbrdo-3a29ee48f82eed3adfd2a90d0eae63a5903149fa.tar.gz
brdo-3a29ee48f82eed3adfd2a90d0eae63a5903149fa.tar.bz2
- Patch #3518404 by bopombatower: lock down DB config based on simpletest UA headers.
Diffstat (limited to 'includes')
-rw-r--r--includes/bootstrap.inc46
-rw-r--r--includes/common.inc6
-rw-r--r--includes/database/database.inc7
3 files changed, 53 insertions, 6 deletions
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index ba88daf7b..7da9cdd83 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -1343,6 +1343,13 @@ function _drupal_bootstrap($phase) {
break;
case DRUPAL_BOOTSTRAP_DATABASE:
+ // The user agent header is used to pass a database prefix in the request when
+ // running tests. However, for security reasons, it is imperative that we
+ // validate we ourselves made the request.
+ if (isset($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'], "simpletest") !== FALSE) && !drupal_valid_test_ua($_SERVER['HTTP_USER_AGENT'])) {
+ header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
+ exit;
+ }
// Initialize the database system. Note that the connection
// won't be initialized until it is actually requested.
require_once DRUPAL_ROOT . '/includes/database/database.inc';
@@ -1429,6 +1436,45 @@ function _drupal_bootstrap($phase) {
}
/**
+ * Validate the HMAC and timestamp of a user agent header from simpletest.
+ */
+function drupal_valid_test_ua($user_agent) {
+ global $databases;
+
+ list($prefix, $time, $salt, $hmac) = explode(';', $user_agent);
+ $check_string = $prefix . ';' . $time . ';' . $salt;
+ // We use the database credentials from settings.php to make the HMAC key, since
+ // the database is not yet initialized and we can't access any Drupal variables.
+ // The file properties add more entropy not easily accessible to others.
+ $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc';
+ $key = sha1(serialize($databases) . filectime($filepath) . fileinode($filepath), TRUE);
+ $time_diff = REQUEST_TIME - $time;
+ // Since we are making a local request, a 2 second time window is allowed,
+ // and the HMAC must match.
+ return (($time_diff >= 0) && ($time_diff < 3) && ($hmac == base64_encode(hash_hmac('sha1', $check_string, $key, TRUE))));
+}
+
+/**
+ * Generate a user agent string with a HMAC and timestamp for simpletest.
+ */
+function drupal_generate_test_ua($prefix) {
+ global $databases;
+ static $key;
+
+ if (!isset($key)) {
+ // We use the database credentials to make the HMAC key, since we
+ // check the HMAC before the database is initialized. filectime()
+ // and fileinode() are not easily determined from remote.
+ $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc';
+ $key = sha1(serialize($databases) . filectime($filepath) . fileinode($filepath), TRUE);
+ }
+ // Generate a moderately secure HMAC based on the database credentials.
+ $salt = uniqid('', TRUE);
+ $check_string = $prefix . ';' . time() . ';' . $salt;
+ return $check_string . ';' . base64_encode(hash_hmac('sha1', $check_string, $key, TRUE));
+}
+
+/**
* Enables use of the theme system without requiring database access.
*
* Loads and initializes the theme system for site installs, updates and when
diff --git a/includes/common.inc b/includes/common.inc
index 4f36a7d8d..0906f8714 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -552,8 +552,8 @@ function drupal_http_request($url, array $options = array()) {
// user-agent is used to ensure that multiple testing sessions running at the
// same time won't interfere with each other as they would if the database
// prefix were stored statically in a file or database variable.
- if (is_string($db_prefix) && preg_match("/^simpletest\d+/", $db_prefix, $matches)) {
- $options['headers']['User-Agent'] = $matches[0];
+ if (is_string($db_prefix) && preg_match("/simpletest\d+/", $db_prefix, $matches)) {
+ $options['headers']['User-Agent'] = drupal_generate_test_ua($matches[0]);
}
$request = $options['method'] . ' ' . $path . " HTTP/1.0\r\n";
@@ -809,7 +809,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
// When running inside the testing framework, we relay the errors
// to the tested site by the way of HTTP headers.
- if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^simpletest\d+$/", $_SERVER['HTTP_USER_AGENT']) && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
+ if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^simpletest\d+;/", $_SERVER['HTTP_USER_AGENT']) && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
// $number does not use drupal_static as it should not be reset
// as it uniquely identifies each PHP error.
static $number = 0;
diff --git a/includes/database/database.inc b/includes/database/database.inc
index 69f84a446..c18aec2f5 100644
--- a/includes/database/database.inc
+++ b/includes/database/database.inc
@@ -1347,9 +1347,10 @@ abstract class Database {
}
// We need to pass around the simpletest database prefix in the request
- // and we put that in the user_agent header.
- if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^simpletest\d+$/", $_SERVER['HTTP_USER_AGENT'])) {
- $db_prefix .= $_SERVER['HTTP_USER_AGENT'];
+ // and we put that in the user_agent header. The header HMAC was already
+ // validated in bootstrap.inc.
+ if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^(simpletest\d+);/", $_SERVER['HTTP_USER_AGENT'], $matches)) {
+ $db_prefix .= $matches[1];
}
return $new_connection;
}