403 after SecureSite login until refresh

haffmans - September 14, 2009 - 12:54
Project:Secure Site
Version:6.x-2.3
Component:Code
Category:bug report
Priority:normal
Assigned:Unassigned
Status:active
Description

I have Drupal 6.13, Securesite 6.x-2.3 and various other modules installed (CCK 6.x-2.2, Views 6.x-2.6 and Organic Groups 6.x-2.0 being the main ones). Securesite is set up to force authentication on restricted pages, using HTTP Basic authentication. The 403 error page is set to a special node. A role (which is assigned to the user) has the "access secured pages" permission assigned to it. Anonymous and Authenticated users do not have this permission.

Whenever I have to log in using SecureSite (thus the HTTP Authentication), I get a 403 page until the next refresh - the user does have access to the requested page.

I've tested as follows:

  1. Log out of Drupal and clean HTTP Authentication
  2. Try to access a restricted page (e.g. 'admin')
  3. First a 401 is triggered, asking me to log in. I log in using the first user (uid = 1)

As a result, the 403 page is shown, instead of the requested page. If I request the page again within the same browser session, it will show up properly. Placing the username/password in the URL causes the same issue. I've also tried changing the "Force Authentication" setting to "Always", but this results in the same problem if a restricted page is requested (pages accessible by anonymous users work fine). The problem also occurs when assigning the "access secured pages" permission to Anonymous and Authenticated users. Disabling, uninstalling and reinstalling the module did not help either.

Watchdog log entries show repeatedly (for each attempt that was made) that:
- The user's session was opened
- An Access Denied entry for the requested URL (with the log entry showing the logged in user, not Anonymous)

This mostly blocks non-browser clients (wget, RSS readers, etc) from accessing the restricted pages properly - as some of them are feeds, it is necessary for me to have it all working properly. In e.g. Firefox the problem also appears, but as said clicking to another page or requesting the same page again results in a proper 200 OK.

Is there an issue with the installation somehow, or is this actually a bug?

#1

Truff - September 18, 2009 - 15:46

I'm experiencing the same issue.

I started with a clean install of Drupal 6.14 and Securesite 6.x-2.3 was the first module I installed. I tested and everything worked as expected. As I installed later modules I began to experience the 403 error after logging in as described above, with a refresh being needed to show the page properly.

I'm fortunate that I had only spent a couple of hours on the site before encoutering this problem, so I will be able to start again and test thoroughly after each new module or configuration change. I'll let you know if I find anything.

#2

Truff - September 22, 2009 - 10:50

I found a solution, but I'm not yet certain of the consequences. On my site I have securesite set to Always force authentication. I don't want any content accessible to anonymous users, and so naturally I denied anonymous users the 'access content' permission, however if I allow it the problem described above disappears. The securesite login is still presented when content is accessed and everything appears to still be secured by it - is this how it's supposed to work in this situation and is it entirely secure?

#3

haffmans - September 24, 2009 - 19:35

That doesn't solve the issue for me - general content needs to remain available by anonymous users in my situation. Removing the "Access content" permission would make that impossible. I want only a limited part of the site to be protected.

#4

ikogan - September 26, 2009 - 08:40

I started experiencing this problem today after installing Organic Groups. I've narrowed this problem down to Organic Groups itself. It's fairly easy to test:

1. Try to access secure content: Failure.
2. Disable Organic Groups, try to access secure content: Success.
3. Enable Organic Groups, try to access secure content: Failure.

What's strange is that the content I'm trying to access isn't even handled by Organic Groups, it's not even a node!

#5

haffmans - September 27, 2009 - 11:59
Category:support request» bug report

Looks like I'm having the same problem, with Organic Groups installed as well. Setting this issue to a bug report (rather than support request), as it looks like there's a problem with the combination of the two modules.

#6

Truff - September 30, 2009 - 12:18

I can confirm that I'm using Organic Groups too. No problems with SecureSite before OG was installed, discovered the same issue after OG (and several other modules) were installed on site - seems we can pin it down to this one.

#7

ikogan - October 5, 2009 - 05:14

After doing a good bit of debugging, we think we have found the issue.

securesite_boot is doing a DRUPAL_BOOTSTRAP_FULL during the boot process. This ends up calling a lot of hook_init methods in several modules, some of which, like og (and admin, among others), expect to be able to check permissions and what not at this point. Unfortunately, securesite has not yet authenticated the user and setup the $user global. This means that all of these init hooks do not have the correct user information. Organic groups tries to setup its group context which requires figuring out which groups a user has access to and whether or not the current path is accessible to that user. This process goes through the Drupal Menu API which makes calls to several hook_access methods. Some of these end up returning FALSE which causes the menu API to immediately dump a 403. This problem is similar to other related bugs like no CSS on the admin pages if the "admin" module is used right after logging in through securesite for the first time. Admin adds the CSS to the page header only if the current user has access to the admin pages. It does this during it's hook_init which, in this case, is again being called before the user information is setup.

Unfortunately, solving this problem seems to be non-trivial as securesite needs several pieces that are only loaded and setup during a full bootstrap. Here are some relevant stack traces and code snippets:

menu_get_item(): /includes/menu.inc at line 303
menu_get_item(): /includes/menu.inc at line 302
og_determine_context(): /sites/all/modules/og/og.module at line 473
og_determine_context(): /sites/all/modules/og/og.module at line 472
og_init(): /sites/all/modules/og/og.module at line 399
og_init(): /sites/all/modules/og/og.module at line 385
module_invoke_all(): /includes/module.inc at line 471
module_invoke_all(): /includes/module.inc at line 464
_drupal_bootstrap_full(): /includes/common.inc at line 2623
_drupal_bootstrap_full(): /includes/common.inc at line 2593
_drupal_bootstrap(): /includes/bootstrap.inc at line 1078
_drupal_bootstrap(): /includes/bootstrap.inc at line 993
drupal_bootstrap(): /includes/bootstrap.inc at line 989
drupal_bootstrap(): /includes/bootstrap.inc at line 983
securesite_boot(): /sites/all/modules/securesite/securesite.module at line 103
securesite_boot(): /sites/all/modules/securesite/securesite.module at line 98
module_invoke(): /includes/module.inc at line 450
module_invoke(): /includes/module.inc at line 443
bootstrap_invoke_all(): /includes/bootstrap.inc at line 591
bootstrap_invoke_all(): /includes/bootstrap.inc at line 588
_drupal_bootstrap(): /includes/bootstrap.inc at line 1050
_drupal_bootstrap(): /includes/bootstrap.inc at line 993
drupal_bootstrap(): /includes/bootstrap.inc at line 989
drupal_bootstrap(): /includes/bootstrap.inc at line 983
/index.php at line 16

securesite.module: securesite_boot: 103

if ($type !== FALSE && (isset($_SESSION['securesite_repeat']) ? !$_SESSION['securesite_repeat'] : TRUE)) {
    drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
    module_load_include('inc', 'securesite');
    _securesite_boot($type);
}

We're still investigating this and hope to find a solution in the near future.

#8

ikogan - October 5, 2009 - 08:01

Well, we've come up with a preliminary workaround but it has some disadvantages and there's no guarantees it won't break horribly. Because of the way that the Drupal module system works and since the "user" module is not a boot module, an additional redirect is required after authentication for this to work correctly. As far as I can tell, a change to Drupal Core would be required to support manipulating authentication in the way that securesite is doing safely. Anyway, here's the diff and I've attached a patch that may work for you.

It's worth noting that we opted to use a different solution for HTTP Authentication to workaround the above disadvantages (mainly the requirement for a redirect) so this was tested fairly briefly.

--- securesite.inc 2009-10-05 03:51:42.551372134 -0400
+++ securesite.inc.orig 2009-10-05 01:28:28.255376000 -0400
@@ -9,7 +9,7 @@
/**
  * Boot with selected authentication mechanism.
  */
-function _securesite_boot($type) {
+function _securesite_init($type) {
   global $user;
   switch ($type) {
     case SECURESITE_DIGEST:

--- securesite.module 2009-10-05 03:51:42.551372134 -0400
+++ securesite.module.orig 2009-10-05 02:18:58.259364000 -0400
@@ -97,15 +97,9 @@
  */
function securesite_boot() {
   global $user;
-  // Did the user send credentials that we accept?
-  $type = _securesite_mechanism();
-  if ($type !== FALSE && (isset($_SESSION['securesite_repeat']) ? !$_SESSION['securesite_repeat'] : TRUE)) {
-    drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
-    module_load_include('inc', 'securesite');
-    _securesite_boot($type);
-  }
   // If credentials are missing and user is not logged in, request new credentials.
-  elseif (empty($user->uid) && !isset($_SESSION['securesite_guest'])) {
+  $type = _securesite_mechanism();
+  if ($type === FALSE && empty($user->uid) && !isset($_SESSION['securesite_guest'])) {
     unset($_SESSION['securesite_repeat']);
     $types = variable_get('securesite_type', array(SECURESITE_BASIC));
     sort($types, SORT_NUMERIC);
@@ -117,6 +111,26 @@
   }
}

+function securesite_init() {
+  global $user;
+  // Did the user send credentials that we accept?
+  $type = _securesite_mechanism();
+  if ($type !== FALSE && empty($user->uid) && (isset($_SESSION['securesite_repeat']) ? !$_SESSION['securesite_repeat'] : TRUE)) {
+    module_load_include('inc', 'securesite');
+    _securesite_init($type);
+    if(!empty($user->uid)) {
+    $query = $_GET;
+    unset($query['q']);
+   
+    foreach($query as $key => $value) {
+    $query[$key] = "$key=$value";
+    }
+   
+    drupal_goto($_GET['q'],implode("&",$query));
+    }
+  }
+}
+
/**
  * Return the authentication method used by the client, or FALSE if the client
  * did not send credentials.

The below patch is meant to be run from the securesite module directory:

cp securesite.patch ${DOCUMENT_ROOT}/sites/all/modules/securesite
cd ${DOCUMENT_ROOT}/sites/all/modules/securesite
patch < securesite.patch

AttachmentSize
securesite.patch 1.97 KB

#9

haffmans - October 10, 2009 - 11:17

I've applied the patch from #8 against securesite 6.x-2.3 and it seems to solve the problem for me. I'm yet to try it out with 6.x-2.4, but judging from the release notes I guess that should work fine...

#10

haffmans - October 14, 2009 - 20:18

I've now updated to SecureSite 6.x-2.4, and applied the patch. It still works as it should, except that it breaks cron.php from functioning through wget. This is the wget output (I've censored the URL and IP since they're private for the time being, it calls cron.php with the proper --user and --password credentials):

--2009-10-14 22:10:53--  http://www.example.com/drupal/cron.php
Resolving
www.example.com... 0.0.0.0
Connecting to www.example.com|0.0.0.0|:80... connected.
HTTP request sent, awaiting response... 401 Unauthorized
Connecting to www.example.com|0.0.0.0|:80... connected.
HTTP request sent, awaiting response... 302 Found
Location: http://www.example.com/drupal/node [following]
--2009-10-14 22:10:55--  http://www.example.com/drupal/node
Reusing
existing connection to www.example.com:80.
HTTP
request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: “node”

    [ <=>                                                                                                                                                             ] 61,000      --.-K/s   in 0.1s

2009-10-14 22:10:56 (414 KB/s) - “node” saved [61000]

The file "node" contains the homepage of the site. I've also tried this with invalid credentials; this results in a proper 401 Unauthorized.

In a browser, first logging in to the site and then calling cron.php works fine. If cron.php is the first request of the session (or you log out right before calling cron.php), the same behavior as wget is given: you're taken to the homepage of the site after logging in.

Any solution?

#11

ikogan - October 17, 2009 - 23:21

After some more poking around, I think it might be possible to make this work by simply calling module_invoke_all("init") rather than doing a page reload and simply ensuring that securesite_init only runs once (perhaps through a static variable). If I have time, I'll give it a go.

 
 

Drupal is a registered trademark of Dries Buytaert.