". t( 'Allows users to authenticate using FOST/HTTP.' ) ."

\n"; } } function FOSTauth_perm() { // Implementation of hook_perm(). return array('administer http authentication'); } function FOSTauth_menu() { // Implementation of hook_menu(). $items = array(); $items['FOSTauth'] = array( 'page callback' => 'FOSTauth_callback', 'access callback' => 'variable_get', 'access arguments' => array('FOSTauth_status', FALSE), 'type' => MENU_CALLBACK, ); $items['admin/settings/FOSTauth'] = array( 'title' => 'FOST HTTP authentication', 'page callback' => 'drupal_get_form', 'page arguments' => array('FOSTauth_settings'), 'access arguments' => array('administer http authentication'), ); return $items; } function FOSTauth_callback() { // Callback for FOSTauth. Usually called by drupal_access_denied() // through the site_403 variable. global $user; // Is this an anonymous user? if (!$user->uid) { // Get the current path from $_REQUEST['destination'], // drupal_access_denied() put it there. $path = drupal_get_path_alias($_REQUEST['destination']); // This pattern was taken from block_list(). $pattern = '/^('. preg_replace( array( '/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\($|\|)/' ), array( '|', '.*', '\1'. preg_quote( variable_get('site_frontpage', 'node'), '/' ) .'\2' ), preg_quote( variable_get('FOSTauth_pages', ''), '/' ) ) .')$/'; // Do we need to promote HTTP authentication on this path? if (preg_match($pattern, $path)) { FOSTauth_unauthorized(); } } // Do what drupal_access_denied() would've done if we wouldn't've been called. menu_set_active_item(''); } function FOSTauth_settings() { // Callback for the settings page. $form['FOSTauth_status'] = array( '#type' => 'checkbox', '#title' => t('Enable FOST HTTP authentication.'), '#default_value' => variable_get('FOSTauth_status', FALSE), '#description' => t( 'Note: when you enable FOST HTTP authentication, !the-setting will be modified. When you disable it again, the setting will be restored.', array( '!the-setting' => l( t('the 403 (access denied) page setting'), 'admin/settings/error-reporting' ) ) ) ); $form['FOSTauth_pages'] = array( '#type' => 'textarea', '#title' => t('Promote FOST HTTP authentication on pages'), '#default_value' => variable_get('FOSTauth_pages', ''), '#description' => t( 'On which pages to promote FOST HTTP authentication, if an anonymous user stumbles upon an access denied page. Enter one page per line as a Drupal path. The * character is a wildcard.' ) ); $form = system_settings_form($form); $form['#submit'][] = 'FOSTauth_settings_form_submit'; return $form; } function FOSTauth_settings_form_submit($form, &$form_state) { // Invoked when this modules's settings are changed. FOSTauth_set_site_403(); } function FOSTauth_set_site_403($disable = FALSE) { /** * Sets the site_403 variable to the appropriate path. * * @param $disable * If TRUE, the site_403 variable will be restored regardless of the FOST * HTTP authentication status. */ $site_403 = variable_get('site_403', ''); if (!$disable && variable_get('FOSTauth_status', FALSE)) { if ($site_403 != 'FOSTauth') { // Save original path. variable_set('FOSTauth_site_403', $site_403); // Set the path to 'FOSTauth' to intercept access denied pages. variable_set('site_403', 'FOSTauth'); } } else if ($site_403 == 'FOSTauth') { // Restore the original path. $original = variable_get('FOSTauth_site_403', ''); variable_set('site_403', $original); } } class Authentication { const doc = "Authenticates the request an authorisation key"; private $username; private $password; private $message; private $authorization; private $key; # associated with username, private $secret; function __construct() { $all_headers = getallheaders(); $normalised_headers = array(); function normalise($string) { return strtoupper(str_replace('_','-',$string)); } foreach ($all_headers as $key => $value) { #header("X-INCOMING-$key: $value"); $normalised_headers[ normalise($key) ] = $value; } $this->authorization = $normalised_headers['AUTHORIZATION']; if (isset($this->authorization)) { list($mechanism, $authz) = explode(' ', $this->authorization); if ($mechanism == 'FOST') { $FOST_Timestamp = $normalised_headers['X-FOST-TIMESTAMP']; $time_array = strptime( substr($FOST_Timestamp, 0, 19), '%Y-%m-%d %H:%M:%S' ); $sent_time = mktime( $time_array['tm_hour'], $time_array['tm_min'], $time_array['tm_sec'], $time_array['tm_mon'] + 1, # 0-11 $time_array['tm_mday'], # 1-31 $time_array['tm_year'] + 1900 # from 1900 ) + date('Z'); # UTC offset $now = time(); if (abs($sent_time - $now) < 300) { list($key,$digest) = explode(':', $authz); $this->key = $key; $this->digest = $digest; $this->username = $normalised_headers['HTTP-X-FOST-USER']; $this->password = 'replace-with-password'; # must be the password of all users if ($this->key) { $header_names = explode( ' ', $normalised_headers['X-FOST-HEADERS'] ); $header_values = array(); foreach ($header_names as $header_name) { $header_value = $normalised_headers[ normalise($header_name) ]; $header_values[] = $header_value; }; $document = sprintf( "%s %s\n%s\n%s\n%s", $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $FOST_Timestamp, implode("\n", $header_values), $_SERVER['QUERY_STRING'] || $HTTP_RAW_POST_DATA # or php://stdin | php://input ); $digest = base64_encode( hash_hmac( 'sha1', $document, 'this needs to be the secret associated with the key', TRUE ) ); if ($digest == $this->digest) { # $logging->info($this->authorization); } else { $this->error(403, "403 Forbidden"); # not 401 Unauthorized } } } else { $this->error(403, "Clock skew too high"); } } } } function error($status, $message) { $this->status = $status; $this->message = $message; } function log_user_in() { $username = $this->username; $password = $this->password; if (isset($username) && isset($password)) { global $user; // Abort if the user with the provided credentials is already logged in. if ($user->uid && strcasecmp($user->name, $username) == 0) { return; } require_once('includes/form.inc'); drupal_load('module', 'user'); if (user_is_blocked($username) || drupal_is_denied('user', $username)) { FOSTauth_unauthorized(); exit; } user_authenticate(array('name' => $username, 'pass' => $password)); // Was authentication successful? if ($user->uid) { // If caching is enabled, 'fork' the current request. if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) { // Continue invocation of hook_boot() for modules coming // after FOSTauth. $invoke_hook = FALSE; foreach (module_list(TRUE, TRUE) as $module) { if ($module == 'FOSTauth') { $invoke_hook = TRUE; } else if ($invoke_hook) { drupal_load('module', $module); module_invoke($module, $hook); } } // Call drupal_page_header(), just like _drupal_bootstrap() would. drupal_page_header(); // Fork the request, bootstrapping will continue in next phase. include('./index.php'); // Don't continue the original request. exit(); } } else { // We need common.inc for t(), and theme.inc for theme() // (called indirectly by t()). require_once('includes/common.inc'); require_once('includes/theme.inc'); watchdog( 'user', 'Login attempt using FOST HTTP authentication failed for %username.', array('%username' => $username) ); FOSTauth_unauthorized(); exit(); } } else if (isset($_GET['authenticate'])) { // Force authentication when requested to do so. FOSTauth_unauthorized(); exit(); } } } function FOSTauth_boot() { // Implementation of hook_boot(). // Return if FOST HTTP authentication is disabled. if (!variable_get('FOSTauth_status', FALSE)) { return; } // Load credentials. $authentication = new Authentication(); $authentication->log_user_in(); } function FOSTauth_unauthorized() { // Set 401 Unauthorized status and WWW-Authenticate header. require_once('includes/common.inc'); require_once('includes/unicode.inc'); $site_name = trim(variable_get('site_name', 'drupal')); $realm = mime_header_encode($site_name); drupal_set_header("HTTP/1.0 403 Forbidden"); } function FOSTauth_parse($header) { // Parse an HTTP authorization header. list($type, $credentials) = split(' ', $header); if ($type == 'Basic') { return explode(':', base64_decode($credentials)); } }