[MERGE] Bug 2305 - Auth ref-counting upgrade

From: Amos Jeffries <squid3_at_treenet.co.nz>
Date: Mon, 24 May 2010 00:43:25 +1200

This bundle updates the previous submission made on 29th April with a
few additional fixes found during testing. The code has now been in
production use for several weeks utilizing the Basic protocol and
external_acl_type authenticators.
  NTLM, Negotiate, and Digest protocols have been through simple lab
tests and passed but were not able to be used for live traffic.

This branch:

  * implements proper RefCounting using the RefCount.h classes for
    almost all auth objects in Squid.

  * Restructures auth objects with a simpler structure of duties and scopes.

  * Prunes away several circular and indirectly circular pointer loops

  * Adds an API to auth config for handling the mainRotate() event. To
only shutdown helpers, fixing the loss of cached credentials on rotate.

  * Adds a username_cache page to cachemgr interface to display the
current usernames and their TTLs to various revalidation or garbage events.

With this we end up with several global pointers for the auth schemes
which have been built into the current Squid. These are RefCount
pointers, fixing the leak of schemes on shutdown. Schemes are now also
permanent structures for the runtime of Squid, fixing leaks on
reconfigure and rotate actions.

These AuthSchemes are responsible for creating auth Config objects for
each auth protocol configured in squid.conf. These config objects are
now also RefCounted as globals, fixing leaks on reconfigure and shutdown.

Each HTTP request authentication attempt generates AuthUserRequest
objects, which may or may not pointer to an AuthUser set of credentials
being checked. AuthUserRequest is RefCounted instead of locked, fixing
several assertion crashes.

AuthUser is now RefCounted instead of locked. It's children inherit
these properties. This simplifies the object handling a lot and fixes
several assertions.
  This also means AuthUser no longer needs a back-pointer to all
AuthUserRequest in order to see if its still needed alive, fixing one
circular lock loop and a few possible assertions.
  The username cache pointers to only AuthUser objects, fixing a second
cirular lock loop and potentially leakage. Also simplifying the hash
cache handling a lot.

Non-Auth code needing a reference to authentication credentials should
hold a pointer to either an AuthUserRequest or AuthUser object. Not any
other auth object.

FUTURE WORK;
   There is still some conditions leading to auth re-challenge when they
are not expected.
  A fair chunk of classes and enums have been shuffled into separate
files to keep the scopes clearer. This could be increased in future when
building the Auth namespace.
  Potential is now present for simpler TTL handling for all auth types.

This work was a collaboration between multiple interested parties over
the last year, with additional developer time funded by Netspace Online
Systems.

Amos

-- 
Please be using
   Current Stable Squid 2.7.STABLE9 or 3.1.3

# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: squid3_at_treenet.co.nz-20100523124004-we1jdklspoat6qpm
# target_branch: http://www.squid-cache.org/bzr/squid3/trunk/
# testament_sha1: d50f12079988af3c7587ed08900387495f87063c
# timestamp: 2010-05-24 00:40:54 +1200
# base_revision_id: squid3_at_treenet.co.nz-20100523000159-\
# 0v91r80nr2bfjmnf
#
# Begin patch
=== modified file 'include/RefCount.h'
--- include/RefCount.h 2009-09-16 09:53:46 +0000
+++ include/RefCount.h 2010-04-08 11:53:16 +0000
@@ -37,6 +37,10 @@
 
 #include "config.h"
 
+#if REFCOUNT_DEBUG
+#include "Debug.h"
+#endif
+
 #if HAVE_IOSTREAM
 #include <iostream>
 #endif
@@ -114,7 +118,7 @@
 struct RefCountable_ {
     RefCountable_():count_(0) {}
 
- virtual ~RefCountable_() {}
+ virtual ~RefCountable_() { assert(count_ == 0); }
 
     /* Not private, to allow class hierarchies */
     void RefCountReference() const {

=== modified file 'src/AuthReg.cc'
--- src/AuthReg.cc 2009-12-26 00:25:57 +0000
+++ src/AuthReg.cc 2010-04-17 10:38:50 +0000
@@ -1,35 +1,43 @@
-#include "squid.h"
-
+#include "config.h"
+#include "Debug.h"
+#include "protos.h"
 
 #if HAVE_AUTH_MODULE_BASIC
 #include "auth/basic/basicScheme.h"
 #endif
-
-#if HAVE_AUTH_MODULE_NTLM
-#include "auth/ntlm/ntlmScheme.h"
-#endif
-
 #if HAVE_AUTH_MODULE_DIGEST
 #include "auth/digest/digestScheme.h"
 #endif
-
 #if HAVE_AUTH_MODULE_NEGOTIATE
 #include "auth/negotiate/negotiateScheme.h"
 #endif
+#if HAVE_AUTH_MODULE_NTLM
+#include "auth/ntlm/ntlmScheme.h"
+#endif
 
+/**
+ * Initialize the authentication modules (if any)
+ * This is required once, before any configuration actions are taken.
+ */
+void
+InitAuthSchemes()
+{
+ debugs(29,1,"Initializing Authentication Schemes ...");
 #if HAVE_AUTH_MODULE_BASIC
-static const char *basic_type = basicScheme::GetInstance().type();
-#endif
-
-#if HAVE_AUTH_MODULE_NTLM
-static const char *ntlm_type = ntlmScheme::GetInstance().type();
-#endif
-
+ static const char *basic_type = basicScheme::GetInstance()->type();
+ debugs(29,1,"Initialized Authentication Scheme '" << basic_type << "'");
+#endif
 #if HAVE_AUTH_MODULE_DIGEST
-static const char *digest_type = digestScheme::GetInstance().type();
+ static const char *digest_type = digestScheme::GetInstance()->type();
+ debugs(29,1,"Initialized Authentication Scheme '" << digest_type << "'");
 #endif
-
 #if HAVE_AUTH_MODULE_NEGOTIATE
-static const char *negotiate_type = negotiateScheme::GetInstance().type();
-#endif
-
+ static const char *negotiate_type = negotiateScheme::GetInstance()->type();
+ debugs(29,1,"Initialized Authentication Scheme '" << negotiate_type << "'");
+#endif
+#if HAVE_AUTH_MODULE_NTLM
+ static const char *ntlm_type = ntlmScheme::GetInstance()->type();
+ debugs(29,1,"Initialized Authentication Scheme '" << ntlm_type << "'");
+#endif
+ debugs(29,1,"Initializing Authentication Schemes Complete.");
+}

=== modified file 'src/CompositePoolNode.h'
--- src/CompositePoolNode.h 2010-05-02 19:32:42 +0000
+++ src/CompositePoolNode.h 2010-05-13 06:20:23 +0000
@@ -41,6 +41,7 @@
 
 #if DELAY_POOLS
 #include "squid.h"
+#include "auth/UserRequest.h"
 #include "DelayPools.h"
 #include "DelayIdComposite.h"
 #include "CommRead.h"
@@ -48,8 +49,6 @@
 
 class StoreEntry;
 
-class AuthUserRequest;
-
 /// \ingroup DelayPoolsAPI
 class CompositePoolNode : public RefCountable, public Updateable
 {
@@ -77,7 +76,7 @@
         CompositeSelectionDetails() {}
 
         Ip::Address src_addr;
- AuthUserRequest *user;
+ AuthUserRequest::Pointer user;
         String tag;
     };
 

=== modified file 'src/DelayUser.cc'
--- src/DelayUser.cc 2009-06-28 12:03:25 +0000
+++ src/DelayUser.cc 2010-04-11 09:02:42 +0000
@@ -180,22 +180,20 @@
 }
 
 void
-DelayUserBucket::operator delete (void *address)
+DelayUserBucket::operator delete(void *address)
 {
- DelayPools::MemoryUsed -= sizeof (DelayUserBucket);
- ::operator delete (address);
+ DelayPools::MemoryUsed -= sizeof(DelayUserBucket);
+ ::operator delete(address);
 }
 
-DelayUserBucket::DelayUserBucket(AuthUser *aUser) : authUser (aUser)
+DelayUserBucket::DelayUserBucket(AuthUser::Pointer aUser) : authUser(aUser)
 {
     debugs(77, 3, "DelayUserBucket::DelayUserBucket");
-
- authUser->lock();
 }
 
 DelayUserBucket::~DelayUserBucket()
 {
- authUser->unlock();
+ authUser = NULL;
     debugs(77, 3, "DelayUserBucket::~DelayUserBucket");
 }
 
@@ -203,10 +201,10 @@
 DelayUserBucket::stats (StoreEntry *entry) const
 {
     storeAppendPrintf(entry, " %s:", authUser->username());
- theBucket.stats (entry);
+ theBucket.stats(entry);
 }
 
-DelayUser::Id::Id(DelayUser::Pointer aDelayUser,AuthUser *aUser) : theUser(aDelayUser)
+DelayUser::Id::Id(DelayUser::Pointer aDelayUser, AuthUser::Pointer aUser) : theUser(aDelayUser)
 {
     theBucket = new DelayUserBucket(aUser);
     DelayUserBucket::Pointer const *existing = theUser->buckets.find(theBucket, DelayUserCmp);

=== modified file 'src/DelayUser.h'
--- src/DelayUser.h 2009-03-08 19:34:36 +0000
+++ src/DelayUser.h 2010-04-11 09:02:42 +0000
@@ -42,6 +42,7 @@
 
 #include "squid.h"
 #include "auth/Gadgets.h"
+#include "auth/User.h"
 #include "CompositePoolNode.h"
 #include "DelayIdComposite.h"
 #include "DelayBucket.h"
@@ -59,10 +60,10 @@
     void operator delete (void *);
 
     void stats(StoreEntry *)const;
- DelayUserBucket(AuthUser *);
+ DelayUserBucket(AuthUser::Pointer);
     ~DelayUserBucket();
     DelayBucket theBucket;
- AuthUser *authUser;
+ AuthUser::Pointer authUser;
 };
 
 /// \ingroup DelayPoolsAPI
@@ -91,7 +92,7 @@
     public:
         void *operator new(size_t);
         void operator delete (void *);
- Id (RefCount<DelayUser>, AuthUser *);
+ Id(RefCount<DelayUser>, AuthUser::Pointer);
         ~Id();
         virtual int bytesWanted (int min, int max) const;
         virtual void bytesIn(int qty);

=== modified file 'src/HttpRequest.cc'
--- src/HttpRequest.cc 2010-04-08 00:12:58 +0000
+++ src/HttpRequest.cc 2010-04-08 12:14:42 +0000
@@ -126,7 +126,7 @@
     // points to a pipe that is owned and initiated by another object.
     body_pipe = NULL;
 
- AUTHUSERREQUESTUNLOCK(auth_user_request, "request");
+ auth_user_request = NULL;
 
     safe_free(canonical);
 
@@ -596,10 +596,7 @@
     // may eventually need cloneNullAdaptationImmune() for that.
     flags = aReq->flags.cloneAdaptationImmune();
 
- if (aReq->auth_user_request) {
- auth_user_request = aReq->auth_user_request;
- AUTHUSERREQUESTLOCK(auth_user_request, "inheritProperties");
- }
+ auth_user_request = aReq->auth_user_request;
 
     if (aReq->pinned_connection) {
         pinned_connection = cbdataReference(aReq->pinned_connection);

=== modified file 'src/HttpRequest.h'
--- src/HttpRequest.h 2010-04-17 02:29:04 +0000
+++ src/HttpRequest.h 2010-04-17 10:38:50 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -151,7 +150,7 @@
 public:
     Ip::Address host_addr;
 
- AuthUserRequest *auth_user_request;
+ AuthUserRequest::Pointer auth_user_request;
 
     u_short port;
 

=== modified file 'src/Makefile.am'
--- src/Makefile.am 2010-05-02 18:51:26 +0000
+++ src/Makefile.am 2010-05-13 06:20:23 +0000
@@ -154,8 +154,6 @@
 
 # libraries used by many targets
 COMMON_LIBS = \
- base/libbase.la \
- libsquid.la \
         auth/libacls.la \
         ident/libident.la \
         acl/libacls.la \
@@ -163,6 +161,8 @@
         acl/libstate.la \
         auth/libauth.la \
         acl/libapi.la \
+ base/libbase.la \
+ libsquid.la \
         ip/libip.la \
         fs/libfs.la
 
@@ -755,7 +755,7 @@
 globals.cc: globals.h mk-globals-c.awk
         $(AWK) -f $(srcdir)/mk-globals-c.awk < $(srcdir)/globals.h > $@ || ($(RM) -f $@ && exit 1)
 
-## Generate files containing strng arrays for various enums....
+## Generate files containing string arrays for various enums....
 hier_code.cc: hier_code.h mk-string-arrays.awk
         $(AWK) -f $(srcdir)/mk-string-arrays.awk < $(srcdir)/hier_code.h > $@ || ($(RM) -f $@ && exit 1)
 
@@ -1105,6 +1105,7 @@
 
 ## Tests of the CacheManager module.
 tests_testCacheManager_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
         debug.cc \
         HttpRequest.cc \
         HttpRequestMethod.cc \
@@ -1284,6 +1285,7 @@
 
 ## Tests of the Even module.
 tests_testEvent_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
         debug.cc \
         EventLoop.h \
         EventLoop.cc \
@@ -1439,6 +1441,7 @@
 
 ## Tests of the EventLoop module.
 tests_testEventLoop_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
         debug.cc \
         EventLoop.h \
         EventLoop.cc \
@@ -1592,6 +1595,7 @@
         $(SQUID_CPPUNIT_LA)
 
 tests_test_http_range_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
         tests/test_http_range.cc \
         BodyPipe.cc \
         cache_cf.cc \
@@ -1742,6 +1746,7 @@
 
 ## Tests of the HttpRequest module.
 tests_testHttpRequest_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
         debug.cc \
         HttpRequest.cc \
         HttpRequestMethod.cc \
@@ -2113,6 +2118,7 @@
 ## Tests of the URL module.
 ## TODO: Trim this down once the insanity is over.
 tests_testURL_SOURCES = \
+ $(ACL_REGISTRATION_SOURCES) \
         debug.cc \
         url.cc \
         URLScheme.cc \

=== modified file 'src/acl/FilledChecklist.cc'
--- src/acl/FilledChecklist.cc 2010-03-22 10:00:14 +0000
+++ src/acl/FilledChecklist.cc 2010-05-04 12:33:30 +0000
@@ -8,95 +8,20 @@
 
 CBDATA_CLASS_INIT(ACLFilledChecklist);
 
-#if MOVED
-int
-ACLFilledChecklist::authenticated()
-{
- http_hdr_type headertype;
-
- if (NULL == request) {
- fatal ("requiresRequest SHOULD have been true for this ACL!!");
- return 0;
- } else if (request->flags.accelerated) {
- /* WWW authorization on accelerated requests */
- headertype = HDR_AUTHORIZATION;
- } else if (request->flags.intercepted || request->flags.spoof_client_ip) {
- debugs(28, DBG_IMPORTANT, HERE << " authentication not applicable on intercepted requests.");
- return -1;
- } else {
- /* Proxy authorization on proxy requests */
- headertype = HDR_PROXY_AUTHORIZATION;
- }
-
- /* get authed here */
- /* Note: this fills in auth_user_request when applicable */
- /*
- * DPW 2007-05-08
- * tryToAuthenticateAndSetAuthUser used to try to lock and
- * unlock auth_user_request on our behalf, but it was too
- * ugly and hard to follow. Now we do our own locking here.
- *
- * I'm not sure what tryToAuthenticateAndSetAuthUser does when
- * auth_user_request is set before calling. I'm tempted to
- * unlock and set it to NULL, but it seems safer to save the
- * pointer before calling and unlock it afterwards. If the
- * pointer doesn't change then its a no-op.
- */
- AuthUserRequest *old_auth_user_request = auth_user_request;
- auth_acl_t result = AuthUserRequest::tryToAuthenticateAndSetAuthUser (&auth_user_request, headertype, request, conn(), src_addr);
- if (auth_user_request)
- AUTHUSERREQUESTLOCK(auth_user_request, "ACLFilledChecklist");
- AUTHUSERREQUESTUNLOCK(old_auth_user_request, "old ACLFilledChecklist");
- switch (result) {
-
- case AUTH_ACL_CANNOT_AUTHENTICATE:
- debugs(28, 4, "aclMatchAcl: returning 0 user authenticated but not authorised.");
- return 0;
-
- case AUTH_AUTHENTICATED:
-
- return 1;
- break;
-
- case AUTH_ACL_HELPER:
- debugs(28, 4, "aclMatchAcl: returning 0 sending credentials to helper.");
- changeState (ProxyAuthLookup::Instance());
- return 0;
-
- case AUTH_ACL_CHALLENGE:
- debugs(28, 4, "aclMatchAcl: returning 0 sending authentication challenge.");
- changeState (ProxyAuthNeeded::Instance());
- return 0;
-
- default:
- fatal("unexpected authenticateAuthenticate reply\n");
- return 0;
- }
-}
-#endif
-
 void
 ACLFilledChecklist::checkCallback(allow_t answer)
 {
- debugs(28, 5, "ACLFilledChecklist::checkCallback: " << this << " answer=" << answer);
+ debugs(28, 5, HERE << this << " answer=" << answer);
 
     /* During reconfigure, we can end up not finishing call
      * sequences into the auth code */
 
- if (auth_user_request) {
+ if (auth_user_request != NULL) {
         /* the filled_checklist lock */
- AUTHUSERREQUESTUNLOCK(auth_user_request, "ACLFilledChecklist");
-
+ auth_user_request = NULL;
         /* it might have been connection based */
- /*
- * DPW 2007-05-08
- * yuck, this make me uncomfortable. why do this here?
- * ConnStateData will do its own unlocking.
- */
- /* BUG 2827: the connection may also not exist. ie fast ACL tests vs client disconnection. */
         if (conn()) {
- AUTHUSERREQUESTUNLOCK(conn()->auth_user_request, "conn via ACLFilledChecklist");
- conn()->auth_type = AUTH_BROKEN;
+ conn()->auth_user_request = NULL;
         }
     }
 
@@ -159,10 +84,6 @@
 
     HTTPMSGUNLOCK(reply);
 
- // no auth_user_request in builds without any Authentication configured
- if (auth_user_request)
- AUTHUSERREQUESTUNLOCK(auth_user_request, "ACLFilledChecklist destructor");
-
     cbdataReferenceDone(conn_);
 
     debugs(28, 4, HERE << "ACLFilledChecklist destroyed " << this);

=== modified file 'src/acl/FilledChecklist.h'
--- src/acl/FilledChecklist.h 2010-04-17 02:29:04 +0000
+++ src/acl/FilledChecklist.h 2010-04-17 10:38:50 +0000
@@ -2,8 +2,8 @@
 #define SQUID_ACLFILLED_CHECKLIST_H
 
 #include "acl/Checklist.h"
+#include "auth/UserRequest.h"
 
-class AuthUserRequest;
 class ExternalACLEntry;
 class ConnStateData;
 
@@ -53,7 +53,7 @@
     HttpReply *reply;
 
     char rfc931[USER_IDENT_SZ];
- AuthUserRequest *auth_user_request;
+ AuthUserRequest::Pointer auth_user_request;
 
 #if SQUID_SNMP
     char *snmp_community;

=== modified file 'src/adaptation/icap/ModXact.cc'
--- src/adaptation/icap/ModXact.cc 2010-04-17 02:29:04 +0000
+++ src/adaptation/icap/ModXact.cc 2010-04-17 10:38:50 +0000
@@ -1279,12 +1279,11 @@
 
 void Adaptation::Icap::ModXact::makeUsernameHeader(const HttpRequest *request, MemBuf &buf)
 {
- if (const AuthUserRequest *auth = request->auth_user_request) {
- if (char const *name = auth->username()) {
- const char *value = TheConfig.client_username_encode ?
- base64_encode(name) : name;
- buf.Printf("%s: %s\r\n", TheConfig.client_username_header,
- value);
+ if (request->auth_user_request != NULL) {
+ char const *name = (request->auth_user_request)->username();
+ if (name) {
+ const char *value = TheConfig.client_username_encode ? base64_encode(name) : name;
+ buf.Printf("%s: %s\r\n", TheConfig.client_username_header, value);
         }
     }
 }

=== modified file 'src/auth/Acl.cc'
--- src/auth/Acl.cc 2009-07-15 11:35:00 +0000
+++ src/auth/Acl.cc 2010-04-11 09:02:42 +0000
@@ -32,25 +32,9 @@
 
     /* get authed here */
     /* Note: this fills in auth_user_request when applicable */
- /*
- * DPW 2007-05-08
- * tryToAuthenticateAndSetAuthUser used to try to lock and
- * unlock auth_user_request on our behalf, but it was too
- * ugly and hard to follow. Now we do our own locking here.
- *
- * I'm not sure what tryToAuthenticateAndSetAuthUser does when
- * auth_user_request is set before calling. I'm tempted to
- * unlock and set it to NULL, but it seems safer to save the
- * pointer before calling and unlock it afterwards. If the
- * pointer doesn't change then its a no-op.
- */
- AuthUserRequest *old_auth_user_request = checklist->auth_user_request;
- const auth_acl_t result = AuthUserRequest::tryToAuthenticateAndSetAuthUser(
+ const AuthAclState result = AuthUserRequest::tryToAuthenticateAndSetAuthUser(
                                   &checklist->auth_user_request, headertype, request,
                                   checklist->conn(), checklist->src_addr);
- if (checklist->auth_user_request)
- AUTHUSERREQUESTLOCK(checklist->auth_user_request, "ACLAuth::authenticated");
- AUTHUSERREQUESTUNLOCK(old_auth_user_request, "old ACLAuth");
     switch (result) {
 
     case AUTH_ACL_CANNOT_AUTHENTICATE:

=== modified file 'src/auth/AclMaxUserIp.cc'
--- src/auth/AclMaxUserIp.cc 2010-04-17 02:29:04 +0000
+++ src/auth/AclMaxUserIp.cc 2010-04-17 10:38:50 +0000
@@ -112,7 +112,7 @@
  * 1 : Match
  */
 int
-ACLMaxUserIP::match(AuthUserRequest * auth_user_request, Ip::Address const &src_addr)
+ACLMaxUserIP::match(AuthUserRequest::Pointer auth_user_request, Ip::Address const &src_addr)
 {
     /*
      * the logic for flush the ip list when the limit is hit vs keep
@@ -157,7 +157,7 @@
 
     ti = match(checklist->auth_user_request, checklist->src_addr);
 
- AUTHUSERREQUESTUNLOCK(checklist->auth_user_request, "ACLChecklist via ACLMaxUserIP");
+ checklist->auth_user_request = NULL;
 
     return ti;
 }

=== modified file 'src/auth/AclMaxUserIp.h'
--- src/auth/AclMaxUserIp.h 2010-04-17 02:29:04 +0000
+++ src/auth/AclMaxUserIp.h 2010-04-17 10:38:50 +0000
@@ -37,8 +37,7 @@
 
 #include "acl/Acl.h"
 #include "acl/Checklist.h"
-
-class AuthUserRequest;
+#include "auth/UserRequest.h"
 
 /// \ingroup ACLAPI
 class ACLMaxUserIP : public ACL
@@ -69,7 +68,7 @@
     static Prototype RegistryProtoype;
     static ACLMaxUserIP RegistryEntry_;
 
- int match(AuthUserRequest *, Ip::Address const &);
+ int match(AuthUserRequest::Pointer, Ip::Address const &);
     char const *class_;
     int maximum;
 

=== modified file 'src/auth/AclProxyAuth.cc'
--- src/auth/AclProxyAuth.cc 2009-09-16 00:13:28 +0000
+++ src/auth/AclProxyAuth.cc 2010-05-06 11:07:19 +0000
@@ -139,16 +139,12 @@
     ACLFilledChecklist *checklist = Filled(cl);
 
     checklist->asyncInProgress(true);
- debugs(28, 3, "ACLChecklist::checkForAsync: checking password via authenticator");
+ debugs(28, 3, HERE << "checking password via authenticator");
 
- AuthUserRequest *auth_user_request;
     /* make sure someone created auth_user_request for us */
     assert(checklist->auth_user_request != NULL);
- auth_user_request = checklist->auth_user_request;
-
- int validated = authenticateValidateUser(auth_user_request);
- assert(validated);
- auth_user_request->start(LookupDone, checklist);
+ assert(checklist->auth_user_request->valid());
+ checklist->auth_user_request->start(LookupDone, checklist);
 }
 
 void
@@ -161,15 +157,14 @@
     if (result != NULL)
         fatal("AclLookupProxyAuthDone: Old code floating around somewhere.\nMake clean and if that doesn't work, report a bug to the squid developers.\n");
 
- if (!authenticateValidateUser(checklist->auth_user_request) || checklist->conn() == NULL) {
+ if (checklist->auth_user_request == NULL || !checklist->auth_user_request->valid() || checklist->conn() == NULL) {
         /* credentials could not be checked either way
          * restart the whole process */
         /* OR the connection was closed, there's no way to continue */
- AUTHUSERREQUESTUNLOCK(checklist->auth_user_request, "ProxyAuthLookup");
+ checklist->auth_user_request = NULL;
 
         if (checklist->conn() != NULL) {
- AUTHUSERREQUESTUNLOCK(checklist->conn()->auth_user_request, "conn via ProxyAuthLookup"); // DPW discomfort
- checklist->conn()->auth_type = AUTH_BROKEN;
+ checklist->conn()->auth_user_request = NULL;
         }
     }
 
@@ -201,7 +196,7 @@
 ACLProxyAuth::matchForCache(ACLChecklist *cl)
 {
     ACLFilledChecklist *checklist = Filled(cl);
- assert (checklist->auth_user_request);
+ assert (checklist->auth_user_request != NULL);
     return data->match(checklist->auth_user_request->username());
 }
 
@@ -213,20 +208,11 @@
 ACLProxyAuth::matchProxyAuth(ACLChecklist *cl)
 {
     ACLFilledChecklist *checklist = Filled(cl);
- checkAuthForCaching(checklist);
+ if (!authenticateUserAuthenticated(Filled(checklist)->auth_user_request)) {
+ return 0;
+ }
     /* check to see if we have matched the user-acl before */
- int result = cacheMatchAcl(&checklist->auth_user_request->user()->
- proxy_match_cache, checklist);
- AUTHUSERREQUESTUNLOCK(checklist->auth_user_request, "ACLChecklist via ACLProxyAuth");
+ int result = cacheMatchAcl(&checklist->auth_user_request->user()->proxy_match_cache, checklist);
+ checklist->auth_user_request = NULL;
     return result;
 }
-
-void
-ACLProxyAuth::checkAuthForCaching(ACLChecklist *checklist)const
-{
- /* for completeness */
- /* consistent parameters ? */
- assert(authenticateUserAuthenticated(Filled(checklist)->auth_user_request));
- /* this check completed */
-}
-

=== modified file 'src/auth/AclProxyAuth.h'
--- src/auth/AclProxyAuth.h 2009-03-31 12:39:30 +0000
+++ src/auth/AclProxyAuth.h 2010-05-05 12:25:14 +0000
@@ -82,7 +82,7 @@
     virtual bool empty () const;
     virtual bool requiresRequest() const {return true;}
 
- virtual ACL *clone()const;
+ virtual ACL *clone() const;
     virtual int matchForCache(ACLChecklist *checklist);
 
 private:
@@ -91,7 +91,6 @@
     static Prototype RegexRegistryProtoype;
     static ACLProxyAuth RegexRegistryEntry_;
     int matchProxyAuth(ACLChecklist *);
- void checkAuthForCaching(ACLChecklist *) const;
     ACLData<char const *> *data;
     char const *type_;
 };

=== added file 'src/auth/AuthAclState.h'
--- src/auth/AuthAclState.h 1970-01-01 00:00:00 +0000
+++ src/auth/AuthAclState.h 2010-04-11 09:02:42 +0000
@@ -0,0 +1,11 @@
+#ifndef _SQUID__SRC_AUTH_AUTHACLSTATE_H
+#define _SQUID__SRC_AUTH_AUTHACLSTATE_H
+
+typedef enum {
+ AUTH_ACL_CHALLENGE = -2,
+ AUTH_ACL_HELPER = -1,
+ AUTH_ACL_CANNOT_AUTHENTICATE = 0,
+ AUTH_AUTHENTICATED = 1
+} AuthAclState;
+
+#endif

=== added file 'src/auth/AuthType.h'
--- src/auth/AuthType.h 1970-01-01 00:00:00 +0000
+++ src/auth/AuthType.h 2010-04-11 09:02:42 +0000
@@ -0,0 +1,15 @@
+#ifndef _SQUID__SRC_AUTH_AUTHTYPE_H
+#define _SQUID__SRC_AUTH_AUTHTYPE_H
+
+typedef enum {
+ AUTH_UNKNOWN, /* default */
+ AUTH_BASIC,
+ AUTH_NTLM,
+ AUTH_DIGEST,
+ AUTH_NEGOTIATE,
+ AUTH_BROKEN /* known type, but broken data */
+} AuthType;
+
+extern const char *AuthType_str[];
+
+#endif

=== modified file 'src/auth/Config.cc'
--- src/auth/Config.cc 2009-02-24 23:52:44 +0000
+++ src/auth/Config.cc 2010-01-17 11:58:04 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -37,12 +36,15 @@
 #include "auth/Config.h"
 #include "auth/UserRequest.h"
 
-/* Get Auth User: Return a filled out auth_user structure for the given
+Auth::authConfig Auth::TheConfig;
+
+/**
+ * Get Auth User: Return a filled out auth_user structure for the given
  * Proxy Auth (or Auth) header. It may be a cached Auth User or a new
  * Unauthenticated structure. The structure is given an inital lock here.
  * It may also be NULL reflecting that no user could be created.
  */
-AuthUserRequest *
+AuthUserRequest::Pointer
 AuthConfig::CreateAuthUser(const char *proxy_auth)
 {
     assert(proxy_auth != NULL);
@@ -55,20 +57,13 @@
         return NULL;
     }
 
- AuthUserRequest *result = config->decode (proxy_auth);
-
- /*
- * DPW 2007-05-08
- * Do not lock the AuthUserRequest on the caller's behalf.
- * Callers must manage their own locks.
- */
- return result;
+ return config->decode(proxy_auth);
 }
 
 AuthConfig *
 AuthConfig::Find(const char *proxy_auth)
 {
- for (authConfig::iterator i = Config.authConfiguration.begin(); i != Config.authConfiguration.end(); ++i)
+ for (Auth::authConfig::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i)
         if (strncasecmp(proxy_auth, (*i)->type(), strlen((*i)->type())) == 0)
             return *i;
 

=== modified file 'src/auth/Config.h'
--- src/auth/Config.h 2009-02-24 23:52:44 +0000
+++ src/auth/Config.h 2010-04-26 07:07:44 +0000
@@ -32,10 +32,13 @@
 #ifndef SQUID_AUTHCONFIG_H
 #define SQUID_AUTHCONFIG_H
 
-class AuthUserRequest;
+#include "auth/UserRequest.h"
+#include "HelperChildConfig.h"
+
 class StoreEntry;
 class HttpReply;
 class HttpRequest;
+class wordlist;
 
 /* for http_hdr_type parameters-by-value */
 #include "HttpHeader.h"
@@ -56,10 +59,10 @@
 {
 
 public:
- static AuthUserRequest *CreateAuthUser (const char *proxy_auth);
+ static AuthUserRequest::Pointer CreateAuthUser(const char *proxy_auth);
 
     static AuthConfig *Find(const char *proxy_auth);
- AuthConfig() {}
+ AuthConfig() : authenticateChildren(20), authenticate(NULL) {}
 
     virtual ~AuthConfig() {}
 
@@ -81,7 +84,7 @@
      \param proxy_auth Login Pattern to parse.
      \retval * Details needed to authenticate.
      */
- virtual AuthUserRequest *decode(char const *proxy_auth) = 0;
+ virtual AuthUserRequest::Pointer decode(char const *proxy_auth) = 0;
 
     /**
      * squid is finished with this config, release any unneeded resources.
@@ -103,13 +106,19 @@
     virtual bool configured() const = 0;
 
     /**
+ * Shutdown just the auth helpers.
+ * For use by log rotate etc. where auth needs to stay running, with the helpers restarted.
+ */
+ virtual void rotateHelpers(void) = 0;
+
+ /**
      * Responsible for writing to the StoreEntry the configuration parameters that a user
      * would put in a config file to recreate the running configuration.
      */
     virtual void dump(StoreEntry *, const char *, AuthConfig *) = 0;
 
     /** add headers as needed when challenging for auth */
- virtual void fixHeader(AuthUserRequest *, HttpReply *, http_hdr_type, HttpRequest *) = 0;
+ virtual void fixHeader(AuthUserRequest::Pointer, HttpReply *, http_hdr_type, HttpRequest *) = 0;
     /** prepare to handle requests */
     virtual void init(AuthConfig *) = 0;
     /** expose any/all statistics to a CacheManager */
@@ -118,6 +127,19 @@
     virtual void parse(AuthConfig *, int, char *) = 0;
     /** the http string id */
     virtual const char * type() const = 0;
+
+public:
+ HelperChildConfig authenticateChildren;
+ wordlist *authenticate;
 };
 
+namespace Auth
+{
+
+typedef Vector<AuthConfig *> authConfig;
+
+extern authConfig TheConfig;
+
+}; // namespace Auth
+
 #endif /* SQUID_AUTHCONFIG_H */

=== modified file 'src/auth/Gadgets.cc'
--- src/auth/Gadgets.cc 2009-03-08 19:34:36 +0000
+++ src/auth/Gadgets.cc 2010-04-26 07:07:44 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -57,11 +56,11 @@
 {
     int rv = 0;
 
- for (authConfig::iterator i = Config.authConfiguration.begin(); i != Config.authConfiguration.end(); ++i)
+ for (Auth::authConfig::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i)
         if ((*i)->configured())
             ++rv;
 
- debugs(29, 9, "authenticateActiveSchemeCount: " << rv << " active.");
+ debugs(29, 9, HERE << rv << " active.");
 
     return rv;
 }
@@ -69,119 +68,84 @@
 int
 authenticateSchemeCount(void)
 {
- int rv = AuthScheme::Schemes().size();
+ int rv = AuthScheme::GetSchemes().size();
 
- debugs(29, 9, "authenticateSchemeCount: " << rv << " active.");
+ debugs(29, 9, HERE << rv << " active.");
 
     return rv;
 }
 
 static void
-authenticateRegisterWithCacheManager(authConfig * config)
+authenticateRegisterWithCacheManager(Auth::authConfig * config)
 {
- for (authConfig::iterator i = config->begin(); i != config->end(); ++i) {
+ for (Auth::authConfig::iterator i = config->begin(); i != config->end(); ++i) {
         AuthConfig *scheme = *i;
         scheme->registerWithCacheManager();
     }
 }
 
 void
-authenticateInit(authConfig * config)
+authenticateInit(Auth::authConfig * config)
 {
- for (authConfig::iterator i = config->begin(); i != config->end(); ++i) {
- AuthConfig *scheme = *i;
-
- if (scheme->configured())
- scheme->init(scheme);
+ /* Do this first to clear memory and remove dead state on a reconfigure */
+ if (proxy_auth_username_cache)
+ AuthUser::CachedACLsReset();
+
+ /* If we do not have any auth config state to create stop now. */
+ if (!config)
+ return;
+
+ for (Auth::authConfig::iterator i = config->begin(); i != config->end(); ++i) {
+ AuthConfig *schemeCfg = *i;
+
+ if (schemeCfg->configured())
+ schemeCfg->init(schemeCfg);
     }
 
     if (!proxy_auth_username_cache)
         AuthUser::cacheInit();
- else
- AuthUser::CachedACLsReset();
-
- authenticateRegisterWithCacheManager(&Config.authConfiguration);
-}
-
-void
-authenticateShutdown(void)
-{
- debugs(29, 2, "authenticateShutdown: shutting down auth schemes");
- /* free the cache if we are shutting down */
-
- if (shutting_down) {
- hashFreeItems(proxy_auth_username_cache, AuthUserHashPointer::removeFromCache);
- AuthScheme::FreeAll();
- } else {
- for (AuthScheme::const_iterator i = AuthScheme::Schemes().begin(); i != AuthScheme::Schemes().end(); ++i)
- (*i)->done();
- }
-}
-
-/**
- \retval 0 not in use
- \retval ? in use
- */
-int
-authenticateAuthUserInuse(AuthUser * auth_user)
-{
- assert(auth_user != NULL);
- return auth_user->references;
-}
-
-void
-authenticateAuthUserMerge(AuthUser * from, AuthUser * to)
-{
- to->absorb (from);
-}
-
-/**
- * Cleans all config-dependent data from the auth_user cache.
- \note It DOES NOT Flush the user cache.
- */
-void
-authenticateUserCacheRestart(void)
-{
+
+ authenticateRegisterWithCacheManager(config);
+}
+
+void
+authenticateRotate(void)
+{
+ for (Auth::authConfig::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i)
+ if ((*i)->configured())
+ (*i)->rotateHelpers();
+}
+
+void
+authenticateReset(void)
+{
+ debugs(29, 2, HERE << "Reset authentication State.");
+
+ /* free all username cache entries */
+ hash_first(proxy_auth_username_cache);
     AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
- debugs(29, 3, HERE << "Clearing config dependent cache data.");
- hash_first(proxy_auth_username_cache);
-
     while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
- auth_user = usernamehash->user();
- debugs(29, 5, "authenticateUserCacheRestat: Clearing cache ACL results for user: " << auth_user->username());
+ debugs(29, 5, HERE << "Clearing entry for user: " << usernamehash->user()->username());
+ hash_remove_link(proxy_auth_username_cache, (hash_link *)usernamehash);
+ delete usernamehash;
     }
-}
-
-
-void
-AuthUserHashPointer::removeFromCache(void *usernamehash_p)
-{
- AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(usernamehash_p);
- AuthUser *auth_user = usernamehash->auth_user;
-
- if ((authenticateAuthUserInuse(auth_user) - 1))
- debugs(29, 1, "AuthUserHashPointer::removeFromCache: entry in use - not freeing");
-
- auth_user->unlock();
-
- /** \todo change behaviour - we remove from the auth user list here, and then unlock, and the
- * delete ourselves.
- */
-}
-
-AuthUserHashPointer::AuthUserHashPointer(AuthUser * anAuth_user):
+
+ /* schedule shutdown of the helpers */
+ authenticateRotate();
+
+ /* free current global config details too. */
+ Auth::TheConfig.clean();
+}
+
+AuthUserHashPointer::AuthUserHashPointer(AuthUser::Pointer anAuth_user):
         auth_user(anAuth_user)
 {
     key = (void *)anAuth_user->username();
     next = NULL;
     hash_join(proxy_auth_username_cache, (hash_link *) this);
-
- /** lock for presence in the cache */
- auth_user->lock();
 }
 
-AuthUser *
+AuthUser::Pointer
 AuthUserHashPointer::user() const
 {
     return auth_user;

=== modified file 'src/auth/Gadgets.h'
--- src/auth/Gadgets.h 2009-03-08 21:19:10 +0000
+++ src/auth/Gadgets.h 2010-04-26 07:07:44 +0000
@@ -35,32 +35,36 @@
 
 #include "hash.h"
 #include "MemPool.h"
-#include "typedefs.h" /* for authConfig */
+#include "auth/Config.h"
+#include "auth/User.h"
 
 class AuthUser;
 
 /**
  \ingroup AuthAPI
  *
- * This is used to link auth_users into the username cache.
+ * This is used to link AuthUsers objects into the username cache.
  * Because some schemes may link in aliases to a user,
  * the link is not part of the AuthUser structure itself.
  *
- \todo Inheritance in a struct? this should be a class.
+ * Code must not hold onto copies of these objects.
+ * They may exist only so long as the AuthUser being referenced
+ * is recorded in the cache. Any caller using hash_remove_link
+ * must then delete the AuthUserHashPointer.
  */
-struct AuthUserHashPointer : public hash_link {
+class AuthUserHashPointer : public hash_link {
     /* first two items must be same as hash_link */
 
 public:
- static void removeFromCache (void *anAuthUserHashPointer);
     MEMPROXY_CLASS(AuthUserHashPointer);
 
- AuthUserHashPointer(AuthUser *);
+ AuthUserHashPointer(AuthUser::Pointer);
+ ~AuthUserHashPointer() { auth_user = NULL; };
 
- AuthUser *user() const;
+ AuthUser::Pointer user() const;
 
 private:
- AuthUser *auth_user;
+ AuthUser::Pointer auth_user;
 };
 
 MEMPROXY_CLASS_INLINE(AuthUserHashPointer);
@@ -75,19 +79,22 @@
  */
 typedef void AUTHSSTATS(StoreEntry *);
 
-/**
- \ingroup AuthAPI
- * subsumed by the C++ interface
- \todo does 'subsumed' mean deprecated use a C++ API call?
+/// \ingroup AuthAPI
+extern void authenticateInit(Auth::authConfig *);
+
+/** \ingroup AuthAPI
+ * Remove all idle authentication state. Intended for use by reconfigure.
+ *
+ * Removes the username cache contents and global configuration state.
+ * Stops just short of detaching the auth components completely.
+ *
+ * Currently active requests should finish. Howevee new requests will not use
+ * authentication unless something causes the global config to be rebuilt.
+ * Such as a configure load action adding config and re-running authenticateInit().
  */
-extern void authenticateAuthUserMerge(AuthUser *, AuthUser *);
+extern void authenticateReset(void);
 
-/// \ingroup AuthAPI
-extern void authenticateInit(authConfig *);
-/// \ingroup AuthAPI
-extern void authenticateShutdown(void);
-/// \ingroup AuthAPI
-extern int authenticateAuthUserInuse(AuthUser * auth_user);
+extern void authenticateRotate(void);
 
 /// \ingroup AuthAPI
 extern void authenticateFreeProxyAuthUserACLResults(void *data);
@@ -97,8 +104,6 @@
 extern int authenticateSchemeCount(void);
 
 /// \ingroup AuthAPI
-extern void authenticateUserCacheRestart(void);
-/// \ingroup AuthAPI
 extern void authenticateOnCloseConnection(ConnStateData * conn);
 
 #endif /* SQUID_AUTH_GADGETS_H */

=== modified file 'src/auth/Makefile.am'
--- src/auth/Makefile.am 2009-11-12 01:12:50 +0000
+++ src/auth/Makefile.am 2010-05-02 06:18:21 +0000
@@ -8,17 +8,21 @@
 
 ## authentication framework; this library is always built
 libauth_la_SOURCES = \
+ AuthType.h \
+ AuthType.cc \
         Config.cc \
         Config.h \
+ Gadgets.cc \
+ Gadgets.h \
         Scheme.cc \
         Scheme.h \
+ State.h \
+ State.cc \
         User.h \
         User.cci \
         User.cc \
         UserRequest.h \
- UserRequest.cc \
- Gadgets.cc \
- Gadgets.h
+ UserRequest.cc
 
 libauth_la_LIBADD = $(AUTH_LIBS_TO_BUILD)
 libauth_la_DEPENDENCIES = $(AUTH_LIBS_TO_BUILD)
@@ -31,32 +35,43 @@
         AclMaxUserIp.cc \
         AclMaxUserIp.h \
         AclProxyAuth.cc \
- AclProxyAuth.h
-
+ AclProxyAuth.h \
+ AuthAclState.h
 
 libbasic_la_SOURCES = \
         basic/basicScheme.cc \
         basic/basicScheme.h \
         basic/auth_basic.cc \
- basic/auth_basic.h
+ basic/auth_basic.h \
+ basic/basicUserRequest.cc \
+ basic/basicUserRequest.h
 
 libdigest_la_SOURCES = \
         digest/digestScheme.cc \
         digest/digestScheme.h \
         digest/auth_digest.cc \
- digest/auth_digest.h
+ digest/auth_digest.h \
+ digest/digestUserRequest.cc \
+ digest/digestUserRequest.h
 
 libntlm_la_SOURCES = \
         ntlm/ntlmScheme.cc \
         ntlm/ntlmScheme.h \
         ntlm/auth_ntlm.cc \
- ntlm/auth_ntlm.h
+ ntlm/auth_ntlm.h \
+ ntlm/ntlmUserRequest.cc \
+ ntlm/ntlmUserRequest.h
 
 libnegotiate_la_SOURCES = \
         negotiate/negotiateScheme.cc \
         negotiate/negotiateScheme.h \
         negotiate/auth_negotiate.cc \
- negotiate/auth_negotiate.h
+ negotiate/auth_negotiate.h \
+ negotiate/negotiateUserRequest.cc \
+ negotiate/negotiateUserRequest.h
+
+AuthType.cc: AuthType.h $(top_srcdir)/src/mk-string-arrays.awk
+ $(AWK) -f $(top_srcdir)/src/mk-string-arrays.awk < $(srcdir)/AuthType.h > $@ || (rm -f $@ ; exit 1)
 
 
 TESTS += testHeaders

=== modified file 'src/auth/Scheme.cc'
--- src/auth/Scheme.cc 2009-03-08 19:34:36 +0000
+++ src/auth/Scheme.cc 2010-05-23 12:40:04 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -39,22 +38,22 @@
 #include "auth/Gadgets.h"
 #include "auth/Config.h"
 
-Vector<AuthScheme*> *AuthScheme::_Schemes = NULL;
+Vector<AuthScheme::Pointer> *AuthScheme::_Schemes = NULL;
 
 void
-AuthScheme::AddScheme(AuthScheme &instance)
+AuthScheme::AddScheme(AuthScheme::Pointer instance)
 {
     iterator i = GetSchemes().begin();
 
     while (i != GetSchemes().end()) {
- assert(strcmp((*i)->type(), instance.type()) != 0);
+ assert(strcmp((*i)->type(), instance->type()) != 0);
         ++i;
     }
 
- GetSchemes().push_back (&instance);
+ GetSchemes().push_back(instance);
 }
 
-AuthScheme *
+AuthScheme::Pointer
 AuthScheme::Find(const char *typestr)
 {
     for (iterator i = GetSchemes().begin(); i != GetSchemes().end(); ++i) {
@@ -62,33 +61,32 @@
             return *i;
     }
 
- return NULL;
-}
-
-Vector<AuthScheme *> const &
-AuthScheme::Schemes()
-{
- return GetSchemes();
-}
-
-Vector<AuthScheme*> &
+ return AuthScheme::Pointer(NULL);
+}
+
+Vector<AuthScheme::Pointer> &
 AuthScheme::GetSchemes()
 {
     if (!_Schemes)
- _Schemes = new Vector<AuthScheme *>;
+ _Schemes = new Vector<AuthScheme::Pointer>;
 
     return *_Schemes;
 }
 
-/*
- * called when a graceful shutdown is to occur
- * of each scheme module.
+/**
+ * Called when a graceful shutdown is to occur of each scheme module.
+ * On completion the auth components are to be considered deleted.
+ * None will be available globally. Some may remain around for their
+ * currently active connections to close, but only those active
+ * connections will retain pointers to them.
  */
 void
 AuthScheme::FreeAll()
 {
+// assert(shutting_down);
+
     while (GetSchemes().size()) {
- AuthScheme *scheme = GetSchemes().back();
+ AuthScheme::Pointer scheme = GetSchemes().back();
         GetSchemes().pop_back();
         scheme->done();
     }

=== modified file 'src/auth/Scheme.h'
--- src/auth/Scheme.h 2009-02-24 23:52:44 +0000
+++ src/auth/Scheme.h 2010-02-12 10:51:58 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -34,8 +33,11 @@
 #ifndef SQUID_AUTHSCHEME_H
 #define SQUID_AUTHSCHEME_H
 
-#include "squid.h"
+#include "config.h"
 #include "Array.h"
+#include "RefCount.h"
+
+class AuthConfig;
 
 /**
  \defgroup AuthSchemeAPI Authentication Scheme API
@@ -43,44 +45,60 @@
  */
 
 /**
- \ingroup AuthAPI
- \ingroup AuthSchemeAPI
- \par
+ * \ingroup AuthAPI
+ * \ingroup AuthSchemeAPI
+ * \par
  * I represent an authentication scheme. For now my children
- * store both the scheme metadata, and the scheme configuration.
- \par
+ * store the scheme metadata.
+ * \par
  * Should we need multiple configs of a single scheme,
  * a new class AuthConfiguration should be made, and the
  * config specific calls on AuthScheme moved to it.
  */
-class AuthScheme
+class AuthScheme : public RefCountable
 {
-
-public:
- static void AddScheme(AuthScheme &);
+public:
+ typedef RefCount<AuthScheme> Pointer;
+ typedef Vector<AuthScheme::Pointer>::iterator iterator;
+ typedef Vector<AuthScheme::Pointer>::const_iterator const_iterator;
+
+public:
+ AuthScheme() : initialised (false) {};
+ virtual ~AuthScheme() {};
+
+ static void AddScheme(AuthScheme::Pointer);
+
+ /**
+ * Final termination of all authentication components.
+ * To be used only on shutdown. All global pointers are released.
+ * After this all schemes will appear completely unsupported
+ * until a call to InitAuthModules().
+ * Release the Auth::TheConfig handles instead to disable authentication
+ * without terminiating all support.
+ */
     static void FreeAll();
- static Vector<AuthScheme*> const &Schemes();
- static AuthScheme *Find(const char *);
- typedef Vector<AuthScheme*>::iterator iterator;
- typedef Vector<AuthScheme*>::const_iterator const_iterator;
- AuthScheme() : initialised (false) {}
 
- virtual ~AuthScheme() {}
+ /**
+ * Locate an authentication scheme component by Name.
+ */
+ static AuthScheme::Pointer Find(const char *);
 
     /* per scheme methods */
     virtual char const *type () const = 0;
     virtual void done() = 0;
     virtual AuthConfig *createConfig() = 0;
+
     // Not implemented
     AuthScheme(AuthScheme const &);
     AuthScheme &operator=(AuthScheme const&);
 
+ static Vector<AuthScheme::Pointer> &GetSchemes();
+
 protected:
     bool initialised;
 
 private:
- static Vector<AuthScheme*> &GetSchemes();
- static Vector<AuthScheme*> *_Schemes;
+ static Vector<AuthScheme::Pointer> *_Schemes;
 };
 
 #endif /* SQUID_AUTHSCHEME_H */

=== added file 'src/auth/State.cc'
--- src/auth/State.cc 1970-01-01 00:00:00 +0000
+++ src/auth/State.cc 2010-02-12 10:51:58 +0000
@@ -0,0 +1,11 @@
+#include "config.h"
+#include "auth/State.h"
+
+CBDATA_GLOBAL_TYPE(authenticateStateData);
+
+void
+authenticateStateFree(authenticateStateData * r)
+{
+ r->auth_user_request = NULL;
+ cbdataFree(r);
+}

=== added file 'src/auth/State.h'
--- src/auth/State.h 1970-01-01 00:00:00 +0000
+++ src/auth/State.h 2010-05-06 11:07:19 +0000
@@ -0,0 +1,19 @@
+#ifndef __AUTH_AUTHENTICATE_STATE_T__
+#define __AUTH_AUTHENTICATE_STATE_T__
+
+#include "auth/UserRequest.h"
+
+/**
+ * CBDATA state for NTLM, Negotiate, and Digest stateful authentication.
+ */
+typedef struct {
+ void *data;
+ AuthUserRequest::Pointer auth_user_request;
+ RH *handler;
+} authenticateStateData;
+
+extern CBDATA_GLOBAL_TYPE(authenticateStateData);
+
+extern void authenticateStateFree(authenticateStateData * r);
+
+#endif /* __AUTH_AUTHENTICATE_STATE_T__ */

=== modified file 'src/auth/User.cc'
--- src/auth/User.cc 2010-04-17 02:29:04 +0000
+++ src/auth/User.cc 2010-05-06 11:07:19 +0000
@@ -42,87 +42,127 @@
 #include "acl/Gadgets.h"
 #include "event.h"
 #include "SquidTime.h"
+#include "Store.h"
 
 #if !_USE_INLINE_
 #include "auth/User.cci"
 #endif
 
 // This should be converted into a pooled type. Does not need to be cbdata
-CBDATA_TYPE(auth_user_ip_t);
-
-AuthUser::AuthUser (AuthConfig *aConfig) :
- auth_type (AUTH_UNKNOWN), config(aConfig),
- usernamehash (NULL), ipcount (0), expiretime (0), references (0), username_(NULL)
+CBDATA_TYPE(AuthUserIP);
+
+time_t AuthUser::last_discard = 0;
+
+char *CredentialsState_str[] = { "Unchecked", "Ok", "Pending", "Handshake", "Failed" };
+
+
+AuthUser::AuthUser(AuthConfig *aConfig) :
+ auth_type(AUTH_UNKNOWN),
+ config(aConfig),
+ ipcount(0),
+ expiretime(0),
+ credentials_state(Unchecked),
+ username_(NULL)
 {
     proxy_auth_list.head = proxy_auth_list.tail = NULL;
     proxy_match_cache.head = proxy_match_cache.tail = NULL;
     ip_list.head = ip_list.tail = NULL;
- requests.head = requests.tail = NULL;
- debugs(29, 5, "AuthUser::AuthUser: Initialised auth_user '" << this << "' with refcount '" << references << "'.");
-}
-
-/* Combine two user structs. ONLY to be called from within a scheme
+ debugs(29, 5, "AuthUser::AuthUser: Initialised auth_user '" << this << "'.");
+}
+
+AuthUser::CredentialsState
+AuthUser::credentials() const
+{
+ return credentials_state;
+}
+
+void
+AuthUser::credentials(CredentialsState newCreds)
+{
+ credentials_state = newCreds;
+}
+
+
+/**
+ * Combine two user structs. ONLY to be called from within a scheme
  * module. The scheme module is responsible for ensuring that the
  * two users _can_ be merged without invalidating all the request
  * scheme data. The scheme is also responsible for merging any user
  * related scheme data itself.
  */
 void
-AuthUser::absorb (AuthUser *from)
+AuthUser::absorb(AuthUser::Pointer from)
 {
- AuthUserRequest *auth_user_request;
+
+ /* RefCount children CANNOT be merged like this. The external AuthUser::Pointer's cannot be changed. */
+
+ /* check that we only have the two references:
+ * 1) our function scope
+ * 2) the parsing function scope)
+ */
+ assert(from->RefCountCount() == 2);
+
     /*
- * XXX combine two authuser structs. Incomplete: it should merge
- * in hash references too and ask the module to merge in scheme
- * data
+ * XXX Incomplete: it should merge in hash references too and ask the module to merge in scheme data
+ * dlink_list proxy_auth_list;
+ * dlink_list proxy_match_cache;
      */
+
     debugs(29, 5, "authenticateAuthUserMerge auth_user '" << from << "' into auth_user '" << this << "'.");
- dlink_node *link = from->requests.head;
-
- while (link) {
- auth_user_request = static_cast<AuthUserRequest *>(link->data);
- dlink_node *tmplink = link;
- link = link->next;
- dlinkDelete(tmplink, &from->requests);
- dlinkAddTail(auth_user_request, tmplink, &requests);
- auth_user_request->user(this);
+
+ /* absorb the list of IP address sources (for max_user_ip controls) */
+ AuthUserIP *new_ipdata;
+ while (from->ip_list.head != NULL) {
+ new_ipdata = static_cast<AuthUserIP *>(from->ip_list.head->data);
+
+ /* If this IP has expired - ignore the expensive merge actions. */
+ if (new_ipdata->ip_expiretime + Config.authenticateIpTTL < squid_curtime) {
+ /* This IP has expired - remove from the source list */
+ dlinkDelete(&new_ipdata->node, &(from->ip_list));
+ cbdataFree(new_ipdata);
+ /* catch incipient underflow */
+ from->ipcount--;
+ } else {
+ /* add to our list. replace if already present. */
+ AuthUserIP *ipdata = static_cast<AuthUserIP *>(ip_list.head->data);
+ bool found = false;
+ while (ipdata) {
+ AuthUserIP *tempnode = static_cast<AuthUserIP *>(ipdata->node.next->data);
+
+ if (ipdata->ipaddr == new_ipdata->ipaddr) {
+ /* This IP has already been seen. */
+ found = true;
+ /* update IP ttl and stop searching. */
+ ipdata->ip_expiretime = max(ipdata->ip_expiretime, new_ipdata->ip_expiretime);
+ break;
+ } else if (ipdata->ip_expiretime + Config.authenticateIpTTL < squid_curtime) {
+ /* This IP has expired - cleanup the destination list */
+ dlinkDelete(&ipdata->node, &ip_list);
+ cbdataFree(ipdata);
+ /* catch incipient underflow */
+ assert(ipcount);
+ ipcount--;
+ }
+
+ ipdata = tempnode;
+ }
+
+ if (!found) {
+ /* This ip is not in the seen list. Add it. */
+ dlinkAddTail(&new_ipdata->node, &ipdata->node, &ip_list);
+ ipcount++;
+ /* remove from the source list */
+ dlinkDelete(&new_ipdata->node, &(from->ip_list));
+ from->ipcount--;
+ }
+ }
     }
-
- references += from->references;
- from->references = 0;
- delete from;
 }
 
 AuthUser::~AuthUser()
 {
- AuthUserRequest *auth_user_request;
- dlink_node *link, *tmplink;
- debugs(29, 5, "AuthUser::~AuthUser: Freeing auth_user '" << this << "' with refcount '" << references << "'.");
- assert(references == 0);
- /* were they linked in by username ? */
-
- if (usernamehash) {
- assert(usernamehash->user() == this);
- debugs(29, 5, "AuthUser::~AuthUser: removing usernamehash entry '" << usernamehash << "'");
- hash_remove_link(proxy_auth_username_cache,
- (hash_link *) usernamehash);
- /* don't free the key as we use the same user string as the auth_user
- * structure */
- delete usernamehash;
- }
-
- /* remove any outstanding requests */
- link = requests.head;
-
- while (link) {
- debugs(29, 5, "AuthUser::~AuthUser: removing request entry '" << link->data << "'");
- auth_user_request = static_cast<AuthUserRequest *>(link->data);
- tmplink = link;
- link = link->next;
- dlinkDelete(tmplink, &requests);
- dlinkNodeDelete(tmplink);
- delete auth_user_request;
- }
+ debugs(29, 5, "AuthUser::~AuthUser: Freeing auth_user '" << this << "'.");
+ assert(RefCountCount() == 0);
 
     /* free cached acl results */
     aclCacheMatchFlush(&proxy_match_cache);
@@ -142,10 +182,10 @@
 {
     if (!proxy_auth_username_cache) {
         /* First time around, 7921 should be big enough */
- proxy_auth_username_cache =
- hash_create((HASHCMP *) strcmp, 7921, hash_string);
+ proxy_auth_username_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
         assert(proxy_auth_username_cache);
         eventAdd("User Cache Maintenance", cacheCleanup, NULL, Config.authenticateGCInterval, 1);
+ last_discard = squid_curtime;
     }
 }
 
@@ -153,21 +193,17 @@
 AuthUser::CachedACLsReset()
 {
     /*
- * We walk the hash by username as that is the unique key we use.
      * This must complete all at once, because we are ensuring correctness.
      */
     AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
- char const *username = NULL;
+ AuthUser::Pointer auth_user;
     debugs(29, 3, "AuthUser::CachedACLsReset: Flushing the ACL caches for all users.");
     hash_first(proxy_auth_username_cache);
 
     while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
         auth_user = usernamehash->user();
- username = auth_user->username();
         /* free cached acl results */
         aclCacheMatchFlush(&auth_user->proxy_match_cache);
-
     }
 
     debugs(29, 3, "AuthUser::CachedACLsReset: Finished.");
@@ -182,7 +218,7 @@
      * entries at a time. Lets see how it flys first.
      */
     AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
+ AuthUser::Pointer auth_user;
     char const *username = NULL;
     debugs(29, 3, "AuthUser::cacheCleanup: Cleaning the user cache now");
     debugs(29, 3, "AuthUser::cacheCleanup: Current time: " << current_time.tv_sec);
@@ -192,40 +228,40 @@
         auth_user = usernamehash->user();
         username = auth_user->username();
 
- /* if we need to have inpedendent expiry clauses, insert a module call
+ /* if we need to have indedendent expiry clauses, insert a module call
          * here */
         debugs(29, 4, "AuthUser::cacheCleanup: Cache entry:\n\tType: " <<
                auth_user->auth_type << "\n\tUsername: " << username <<
                "\n\texpires: " <<
                (long int) (auth_user->expiretime + Config.authenticateTTL) <<
- "\n\treferences: " << (long int) auth_user->references);
+ "\n\treferences: " << (long int) auth_user->RefCountCount());
 
         if (auth_user->expiretime + Config.authenticateTTL <= current_time.tv_sec) {
             debugs(29, 5, "AuthUser::cacheCleanup: Removing user " << username << " from cache due to timeout.");
- /* the minus 1 accounts for the cache lock */
 
- if (!(authenticateAuthUserInuse(auth_user) - 1))
- /* we don't warn if we leave the user in the cache,
- * because other modules (ie delay pools) may keep
- * locks on users, and thats legitimate
- */
- auth_user->unlock();
+ /* Old credentials are always removed. Existing users must hold their own
+ * AuthUser::Pointer to the credentials. Cache exists only for finding
+ * and re-using current valid credentials.
+ */
+ hash_remove_link(proxy_auth_username_cache, usernamehash);
+ delete usernamehash;
         }
     }
 
     debugs(29, 3, "AuthUser::cacheCleanup: Finished cleaning the user cache.");
     eventAdd("User Cache Maintenance", cacheCleanup, NULL, Config.authenticateGCInterval, 1);
+ last_discard = squid_curtime;
 }
 
 void
 AuthUser::clearIp()
 {
- auth_user_ip_t *ipdata, *tempnode;
+ AuthUserIP *ipdata, *tempnode;
 
- ipdata = (auth_user_ip_t *) ip_list.head;
+ ipdata = (AuthUserIP *) ip_list.head;
 
     while (ipdata) {
- tempnode = (auth_user_ip_t *) ipdata->node.next;
+ tempnode = (AuthUserIP *) ipdata->node.next;
         /* walk the ip list */
         dlinkDelete(&ipdata->node, &ip_list);
         cbdataFree(ipdata);
@@ -242,7 +278,7 @@
 void
 AuthUser::removeIp(Ip::Address ipaddr)
 {
- auth_user_ip_t *ipdata = (auth_user_ip_t *) ip_list.head;
+ AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
 
     while (ipdata) {
         /* walk the ip list */
@@ -257,7 +293,7 @@
             return;
         }
 
- ipdata = (auth_user_ip_t *) ipdata->node.next;
+ ipdata = (AuthUserIP *) ipdata->node.next;
     }
 
 }
@@ -265,10 +301,10 @@
 void
 AuthUser::addIp(Ip::Address ipaddr)
 {
- auth_user_ip_t *ipdata = (auth_user_ip_t *) ip_list.head;
+ AuthUserIP *ipdata = (AuthUserIP *) ip_list.head;
     int found = 0;
 
- CBDATA_INIT_TYPE(auth_user_ip_t);
+ CBDATA_INIT_TYPE(AuthUserIP);
 
     /*
      * we walk the entire list to prevent the first item in the list
@@ -276,7 +312,7 @@
      * a timeout+reconfigure
      */
     while (ipdata) {
- auth_user_ip_t *tempnode = (auth_user_ip_t *) ipdata->node.next;
+ AuthUserIP *tempnode = (AuthUserIP *) ipdata->node.next;
         /* walk the ip list */
 
         if (ipdata->ipaddr == ipaddr) {
@@ -300,7 +336,7 @@
         return;
 
     /* This ip is not in the seen list */
- ipdata = cbdataAlloc(auth_user_ip_t);
+ ipdata = cbdataAlloc(AuthUserIP);
 
     ipdata->ip_expiretime = squid_curtime;
 
@@ -313,37 +349,47 @@
     debugs(29, 2, "authenticateAuthUserAddIp: user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")");
 }
 
-
-void
-AuthUser::lock()
-{
- debugs(29, 9, "authenticateAuthUserLock auth_user '" << this << "'.");
- assert(this != NULL);
- references++;
- debugs(29, 9, "authenticateAuthUserLock auth_user '" << this << "' now at '" << references << "'.");
-}
-
-void
-AuthUser::unlock()
-{
- debugs(29, 9, "authenticateAuthUserUnlock auth_user '" << this << "'.");
- assert(this != NULL);
-
- if (references > 0) {
- references--;
- } else {
- debugs(29, 1, "Attempt to lower Auth User " << this << " refcount below 0!");
- }
-
- debugs(29, 9, "authenticateAuthUserUnlock auth_user '" << this << "' now at '" << references << "'.");
-
- if (references == 0)
- delete this;
-}
-
-/* addToNameCache: add a auth_user structure to the username cache */
+/**
+ * Add the AuthUser structure to the username cache.
+ */
 void
 AuthUser::addToNameCache()
 {
- usernamehash = new AuthUserHashPointer (this);
+ /* AuthUserHashPointer will self-register with the username cache */
+ new AuthUserHashPointer(this);
+}
+
+/**
+ * Dump the username cache statictics for viewing...
+ */
+void
+AuthUser::UsernameCacheStats(StoreEntry *output)
+{
+ AuthUserHashPointer *usernamehash;
+
+ /* overview of username cache */
+ storeAppendPrintf(output, "Cached Usernames: %d of %d\n", proxy_auth_username_cache->count, proxy_auth_username_cache->size);
+ storeAppendPrintf(output, "Next Garbage Collection in %d seconds.\n", static_cast<int32_t>(last_discard + Config.authenticateGCInterval - squid_curtime));
+
+ /* cache dump column titles */
+ storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\n",
+ "Type",
+ "State",
+ "Check TTL",
+ "Cache TTL",
+ "Username");
+ storeAppendPrintf(output, "--------------- --------- --------- --------- ------------------------------\n");
+
+ hash_first(proxy_auth_username_cache);
+ while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
+ AuthUser::Pointer auth_user = usernamehash->user();
+
+ storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\n",
+ AuthType_str[auth_user->auth_type],
+ CredentialsState_str[auth_user->credentials()],
+ auth_user->ttl(),
+ static_cast<int32_t>(auth_user->expiretime - squid_curtime + Config.authenticateTTL),
+ auth_user->username()
+ );
+ }
 }

=== modified file 'src/auth/User.cci'
--- src/auth/User.cci 2009-02-24 23:52:44 +0000
+++ src/auth/User.cci 2010-04-24 03:42:16 +0000
@@ -47,7 +47,7 @@
 }
 
 void
-AuthUser::username(char const*aString)
+AuthUser::username(char const *aString)
 {
     if (aString) {
         assert(!username_);
@@ -56,14 +56,3 @@
         safe_free(username_);
     }
 }
-
-void
-AuthUser::addRequest(AuthUserRequest *request)
-{
- /* lock for the request link */
-
- lock();
- dlink_node *node = dlinkNodeNew();
-
- dlinkAdd(request, node, &requests);
-}

=== modified file 'src/auth/User.h'
--- src/auth/User.h 2010-05-02 19:32:42 +0000
+++ src/auth/User.h 2010-05-23 11:59:28 +0000
@@ -34,15 +34,14 @@
 #ifndef SQUID_AUTHUSER_H
 #define SQUID_AUTHUSER_H
 
-class AuthUserRequest;
+#include "auth/AuthType.h"
+#include "dlink.h"
+#include "ip/Address.h"
+#include "RefCount.h"
+
 class AuthConfig;
 class AuthUserHashPointer;
-
-/* for auth_type_t */
-#include "enums.h"
-
-#include "ip/Address.h"
-#include "dlink.h"
+class StoreEntry;
 
 /**
  * \ingroup AuthAPI
@@ -52,53 +51,71 @@
  * structure is the cached ACL match results. This structure, is private to
  * the authentication framework.
  */
-class AuthUser
+class AuthUser : public RefCountable
 {
-
 public:
+ typedef RefCount<AuthUser> Pointer;
+
     /* extra fields for proxy_auth */
     /* auth_type and auth_module are deprecated. Do Not add new users of these fields.
      * Aim to remove shortly
      */
     /** \deprecated this determines what scheme owns the user data. */
- auth_type_t auth_type;
+ AuthType auth_type;
     /** the config for this user */
     AuthConfig *config;
- /** we only have one username associated with a given auth_user struct */
- AuthUserHashPointer *usernamehash;
     /** we may have many proxy-authenticate strings that decode to the same user */
     dlink_list proxy_auth_list;
     dlink_list proxy_match_cache;
     size_t ipcount;
     long expiretime;
- /** how many references are outstanding to this instance */
- size_t references;
- /** the auth_user_request structures that link to this. Yes it could be a splaytree
- * but how many requests will a single username have in parallel? */
- dlink_list requests;
 
     static void cacheInit();
     static void CachedACLsReset();
 
- void absorb(AuthUser *from);
+ void absorb(AuthUser::Pointer from);
     virtual ~AuthUser();
     _SQUID_INLINE_ char const *username() const;
     _SQUID_INLINE_ void username(char const *);
+
+ /**
+ * How long these credentials are still valid for.
+ * Negative numbers means already expired.
+ */
+ virtual int32_t ttl() const = 0;
+
+ /* Manage list of IPs using this username */
     void clearIp();
     void removeIp(Ip::Address);
     void addIp(Ip::Address);
- _SQUID_INLINE_ void addRequest(AuthUserRequest *);
-
- void lock();
- void unlock();
 
     void addToNameCache();
+ static void UsernameCacheStats(StoreEntry * output);
+
+ enum CredentialsState { Unchecked, Ok, Pending, Handshake, Failed };
+ CredentialsState credentials() const;
+ void credentials(CredentialsState);
+
+private:
+ /**
+ * The current state these credentials are in:
+ * Unchecked
+ * Authenticated
+ * Pending helper result
+ * Handshake happening in stateful auth.
+ * Failed auth
+ */
+ CredentialsState credentials_state;
 
 protected:
- AuthUser (AuthConfig *);
+ AuthUser(AuthConfig *);
 
 private:
- static void cacheCleanup (void *unused);
+ /**
+ * Garbage Collection for the username cache.
+ */
+ static void cacheCleanup(void *unused);
+ static time_t last_discard; /// Time of last username cache garbage collection.
 
     /**
      * DPW 2007-05-08
@@ -111,6 +128,8 @@
     dlink_list ip_list;
 };
 
+extern char *CredentialsState_str[];
+
 #if _USE_INLINE_
 #include "auth/User.cci"
 #endif

=== modified file 'src/auth/UserRequest.cc'
--- src/auth/UserRequest.cc 2010-04-17 02:29:04 +0000
+++ src/auth/UserRequest.cc 2010-05-06 11:07:19 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -54,27 +53,15 @@
 
 /* Generic Functions */
 
-size_t
-AuthUserRequest::refCount () const
-{
- return references;
-}
-
 char const *
 AuthUserRequest::username() const
 {
- if (user())
+ if (user() != NULL)
         return user()->username();
     else
         return NULL;
 }
 
-size_t
-authenticateRequestRefCount (AuthUserRequest *aRequest)
-{
- return aRequest->refCount();
-}
-
 /**** PUBLIC FUNCTIONS (ALL GENERIC!) ****/
 
 /* send the initial data to an authenticator module */
@@ -82,49 +69,36 @@
 AuthUserRequest::start(RH * handler, void *data)
 {
     assert(handler);
+ assert(data);
     debugs(29, 9, "authenticateStart: auth_user_request '" << this << "'");
     module_start(handler, data);
 }
 
-/*
- * Check a auth_user pointer for validity. Does not check passwords, just data
- * sensability. Broken or Unknown auth_types are not valid for use...
- */
-
-int
-authenticateValidateUser(AuthUserRequest * auth_user_request)
+bool
+AuthUserRequest::valid() const
 {
- debugs(29, 9, "authenticateValidateUser: Validating Auth_user request '" << auth_user_request << "'.");
-
- if (auth_user_request == NULL) {
- debugs(29, 4, "authenticateValidateUser: Auth_user_request was NULL!");
- return 0;
- }
-
- if (auth_user_request->user() == NULL) {
- debugs(29, 4, "authenticateValidateUser: No associated auth_user structure");
- return 0;
- }
-
- if (auth_user_request->user()->auth_type == AUTH_UNKNOWN) {
- debugs(29, 4, "authenticateValidateUser: Auth_user '" << auth_user_request->user() << "' uses unknown scheme.");
- return 0;
- }
-
- if (auth_user_request->user()->auth_type == AUTH_BROKEN) {
- debugs(29, 4, "authenticateValidateUser: Auth_user '" << auth_user_request->user() << "' is broken for it's scheme.");
- return 0;
+ debugs(29, 9, HERE << "Validating AuthUserRequest '" << this << "'.");
+
+ if (user() == NULL) {
+ debugs(29, 4, HERE << "No associated AuthUser data");
+ return false;
+ }
+
+ if (user()->auth_type == AUTH_UNKNOWN) {
+ debugs(29, 4, HERE << "AuthUser '" << user() << "' uses unknown scheme.");
+ return false;
+ }
+
+ if (user()->auth_type == AUTH_BROKEN) {
+ debugs(29, 4, HERE << "AuthUser '" << user() << "' is broken for it's scheme.");
+ return false;
     }
 
     /* any other sanity checks that we need in the future */
 
- /* Thus should a module call to something like authValidate */
-
     /* finally return ok */
- debugs(29, 5, "authenticateValidateUser: Validated Auth_user request '" << auth_user_request << "'.");
-
- return 1;
-
+ debugs(29, 5, HERE << "Validated. AuthUserRequest '" << this << "'.");
+ return true;
 }
 
 void *
@@ -140,51 +114,36 @@
     fatal ("AuthUserRequest child failed to override operator delete\n");
 }
 
-AuthUserRequest::AuthUserRequest():_auth_user(NULL), message(NULL),
- references (0), lastReply (AUTH_ACL_CANNOT_AUTHENTICATE)
+AuthUserRequest::AuthUserRequest():
+ _auth_user(NULL),
+ message(NULL),
+ lastReply(AUTH_ACL_CANNOT_AUTHENTICATE)
 {
- debugs(29, 5, "AuthUserRequest::AuthUserRequest: initialised request " <<
- this);
+ debugs(29, 5, "AuthUserRequest::AuthUserRequest: initialised request " << this);
 }
 
 AuthUserRequest::~AuthUserRequest()
 {
- dlink_node *link;
- debugs(29, 5, "AuthUserRequest::~AuthUserRequest: freeing request " <<
- this);
- assert(references == 0);
-
- if (user()) {
- /* unlink from the auth_user struct */
- link = user()->requests.head;
-
- while (link && (link->data != this))
- link = link->next;
-
- assert(link != NULL);
-
- dlinkDelete(link, &user()->requests);
-
- dlinkNodeDelete(link);
-
- /* unlock the request structure's lock */
- user()->unlock();
-
+ assert(RefCountCount()==0);
+ debugs(29, 5, "AuthUserRequest::~AuthUserRequest: freeing request " << this);
+
+ if (user() != NULL) {
+ /* release our references to the user credentials */
         user(NULL);
     }
 
- safe_free (message);
+ safe_free(message);
 }
 
 void
-AuthUserRequest::setDenyMessage (char const *aString)
+AuthUserRequest::setDenyMessage(char const *aString)
 {
- safe_free (message);
- message = xstrdup (aString);
+ safe_free(message);
+ message = xstrdup(aString);
 }
 
 char const *
-AuthUserRequest::getDenyMessage ()
+AuthUserRequest::getDenyMessage()
 {
     return message;
 }
@@ -200,9 +159,9 @@
 }
 
 static void
-authenticateAuthUserRequestSetIp(AuthUserRequest * auth_user_request, Ip::Address &ipaddr)
+authenticateAuthUserRequestSetIp(AuthUserRequest::Pointer auth_user_request, Ip::Address &ipaddr)
 {
- AuthUser *auth_user = auth_user_request->user();
+ AuthUser::Pointer auth_user = auth_user_request->user();
 
     if (!auth_user)
         return;
@@ -211,9 +170,9 @@
 }
 
 void
-authenticateAuthUserRequestRemoveIp(AuthUserRequest * auth_user_request, Ip::Address const &ipaddr)
+authenticateAuthUserRequestRemoveIp(AuthUserRequest::Pointer auth_user_request, Ip::Address const &ipaddr)
 {
- AuthUser *auth_user = auth_user_request->user();
+ AuthUser::Pointer auth_user = auth_user_request->user();
 
     if (!auth_user)
         return;
@@ -222,17 +181,17 @@
 }
 
 void
-authenticateAuthUserRequestClearIp(AuthUserRequest * auth_user_request)
+authenticateAuthUserRequestClearIp(AuthUserRequest::Pointer auth_user_request)
 {
- if (auth_user_request)
+ if (auth_user_request != NULL)
         auth_user_request->user()->clearIp();
 }
 
 int
-authenticateAuthUserRequestIPCount(AuthUserRequest * auth_user_request)
+authenticateAuthUserRequestIPCount(AuthUserRequest::Pointer auth_user_request)
 {
- assert(auth_user_request);
- assert(auth_user_request->user());
+ assert(auth_user_request != NULL);
+ assert(auth_user_request->user() != NULL);
     return auth_user_request->user()->ipcount;
 }
 
@@ -241,9 +200,9 @@
  * authenticateUserAuthenticated: is this auth_user structure logged in ?
  */
 int
-authenticateUserAuthenticated(AuthUserRequest * auth_user_request)
+authenticateUserAuthenticated(AuthUserRequest::Pointer auth_user_request)
 {
- if (!authenticateValidateUser(auth_user_request))
+ if (auth_user_request == NULL || !auth_user_request->valid())
         return 0;
 
     return auth_user_request->authenticated();
@@ -286,19 +245,19 @@
  * This is basically a handle approach.
  */
 static void
-authenticateAuthenticateUser(AuthUserRequest * auth_user_request, HttpRequest * request, ConnStateData * conn, http_hdr_type type)
+authenticateAuthenticateUser(AuthUserRequest::Pointer auth_user_request, HttpRequest * request, ConnStateData * conn, http_hdr_type type)
 {
- assert(auth_user_request != NULL);
+ assert(auth_user_request.getRaw() != NULL);
 
     auth_user_request->authenticate(request, conn, type);
 }
 
-static AuthUserRequest *
-authTryGetUser (AuthUserRequest **auth_user_request, ConnStateData * conn, HttpRequest * request)
+static AuthUserRequest::Pointer
+authTryGetUser(AuthUserRequest::Pointer auth_user_request, ConnStateData * conn, HttpRequest * request)
 {
- if (*auth_user_request)
- return *auth_user_request;
- else if (request != NULL && request->auth_user_request)
+ if (auth_user_request != NULL)
+ return auth_user_request;
+ else if (request != NULL && request->auth_user_request != NULL)
         return request->auth_user_request;
     else if (conn != NULL)
         return conn->auth_user_request;
@@ -327,8 +286,8 @@
  *
  * Caller is responsible for locking and unlocking their *auth_user_request!
  */
-auth_acl_t
-AuthUserRequest::authenticate(AuthUserRequest ** auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
+AuthAclState
+AuthUserRequest::authenticate(AuthUserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
 {
     const char *proxy_auth;
     assert(headertype != 0);
@@ -342,15 +301,16 @@
      * connection when we recieve no authentication header.
      */
 
- if (((proxy_auth == NULL) && (!authenticateUserAuthenticated(authTryGetUser(auth_user_request,conn,request))))
- || (conn != NULL && conn->auth_type == AUTH_BROKEN)) {
+ /* a) can we find other credentials to use? and b) are they logged in already? */
+ if (proxy_auth == NULL && !authenticateUserAuthenticated(authTryGetUser(*auth_user_request,conn,request))) {
         /* no header or authentication failed/got corrupted - restart */
- debugs(29, 4, "authenticateAuthenticate: broken auth or no proxy_auth header. Requesting auth header.");
+ debugs(29, 4, HERE << "No Proxy-Auth header and no working alternative. Requesting auth header.");
+
         /* something wrong with the AUTH credentials. Force a new attempt */
 
+ /* connection auth we must reset on auth errors */
         if (conn != NULL) {
- conn->auth_type = AUTH_UNKNOWN;
- AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
+ conn->auth_user_request = NULL;
         }
 
         *auth_user_request = NULL;
@@ -362,84 +322,75 @@
      * No check for function required in the if: its compulsory for conn based
      * auth modules
      */
- if (proxy_auth && conn != NULL && conn->auth_user_request &&
+ if (proxy_auth && conn != NULL && conn->auth_user_request != NULL &&
             authenticateUserAuthenticated(conn->auth_user_request) &&
             conn->auth_user_request->connLastHeader() != NULL &&
             strcmp(proxy_auth, conn->auth_user_request->connLastHeader())) {
- debugs(29, 2, "authenticateAuthenticate: DUPLICATE AUTH - authentication header on already authenticated connection!. AU " <<
+ debugs(29, 2, "WARNING: DUPLICATE AUTH - authentication header on already authenticated connection!. AU " <<
                conn->auth_user_request << ", Current user '" <<
                conn->auth_user_request->username() << "' proxy_auth " <<
                proxy_auth);
 
- /* remove this request struct - the link is already authed and it can't be to
- * reauth.
- */
+ /* remove this request struct - the link is already authed and it can't be to reauth. */
 
         /* This should _only_ ever occur on the first pass through
          * authenticateAuthenticate
          */
         assert(*auth_user_request == NULL);
- AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
- /* Set the connection auth type */
- conn->auth_type = AUTH_UNKNOWN;
+ conn->auth_user_request = NULL;
     }
 
     /* we have a proxy auth header and as far as we know this connection has
      * not had bungled connection oriented authentication happen on it. */
- debugs(29, 9, "authenticateAuthenticate: header " << (proxy_auth ? proxy_auth : "-") << ".");
+ debugs(29, 9, HERE << "header " << (proxy_auth ? proxy_auth : "-") << ".");
 
     if (*auth_user_request == NULL) {
- debugs(29, 9, "authenticateAuthenticate: This is a new checklist test on FD:" << (conn != NULL ? conn->fd : -1) );
+ debugs(29, 9, HERE << "This is a new checklist test on FD:" << (conn != NULL ? conn->fd : -1) );
 
- if (proxy_auth && !request->auth_user_request && conn != NULL && conn->auth_user_request) {
+ if (proxy_auth && request->auth_user_request == NULL && conn != NULL && conn->auth_user_request != NULL) {
             AuthConfig * scheme = AuthConfig::Find(proxy_auth);
 
- if (!conn->auth_user_request->user() || conn->auth_user_request->user()->config != scheme) {
- debugs(29, 1, "authenticateAuthenticate: Unexpected change of authentication scheme from '" <<
+ if (conn->auth_user_request->user() == NULL || conn->auth_user_request->user()->config != scheme) {
+ debugs(29, 1, "WARNING: Unexpected change of authentication scheme from '" <<
                        conn->auth_user_request->user()->config->type() <<
                        "' to '" << proxy_auth << "' (client " <<
                        src_addr << ")");
 
- AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
- conn->auth_type = AUTH_UNKNOWN;
+ conn->auth_user_request = NULL;
             }
         }
 
- if ((!request->auth_user_request)
- && (conn == NULL || conn->auth_type == AUTH_UNKNOWN)) {
+ if (request->auth_user_request == NULL && (conn == NULL || conn->auth_user_request == NULL)) {
             /* beginning of a new request check */
- debugs(29, 4, "authenticateAuthenticate: no connection authentication type");
+ debugs(29, 4, HERE << "No connection authentication type");
 
             *auth_user_request = AuthConfig::CreateAuthUser(proxy_auth);
- if (!authenticateValidateUser(*auth_user_request)) {
- if (*auth_user_request == NULL)
- return AUTH_ACL_CHALLENGE;
-
+ if (*auth_user_request == NULL)
+ return AUTH_ACL_CHALLENGE;
+ else if (!(*auth_user_request)->valid()) {
                 /* the decode might have left a username for logging, or a message to
                  * the user */
 
                 if ((*auth_user_request)->username()) {
                     request->auth_user_request = *auth_user_request;
- AUTHUSERREQUESTLOCK(request->auth_user_request, "request");
                 }
 
                 *auth_user_request = NULL;
                 return AUTH_ACL_CHALLENGE;
             }
 
- /* the user_request comes prelocked for the caller to createAuthUser (us) */
- } else if (request->auth_user_request) {
+ } else if (request->auth_user_request != NULL) {
             *auth_user_request = request->auth_user_request;
         } else {
             assert (conn != NULL);
- if (conn->auth_user_request) {
+ if (conn->auth_user_request != NULL) {
                 *auth_user_request = conn->auth_user_request;
             } else {
                 /* failed connection based authentication */
- debugs(29, 4, "authenticateAuthenticate: Auth user request " <<
+ debugs(29, 4, HERE << "Auth user request " <<
                        *auth_user_request << " conn-auth user request " <<
                        conn->auth_user_request << " conn type " <<
- conn->auth_type << " authentication failed.");
+ conn->auth_user_request->user()->auth_type << " authentication failed.");
 
                 *auth_user_request = NULL;
                 return AUTH_ACL_CHALLENGE;
@@ -449,16 +400,14 @@
 
     if (!authenticateUserAuthenticated(*auth_user_request)) {
         /* User not logged in. Log them in */
- authenticateAuthenticateUser(*auth_user_request, request,
- conn, headertype);
+ authenticateAuthenticateUser(*auth_user_request, request, conn, headertype);
 
         switch (authenticateDirection(*auth_user_request)) {
 
         case 1:
 
- if (NULL == request->auth_user_request) {
+ if (request->auth_user_request == NULL) {
                 request->auth_user_request = *auth_user_request;
- AUTHUSERREQUESTLOCK(request->auth_user_request, "request");
             }
 
             /* fallthrough to -2 */
@@ -481,7 +430,6 @@
             if ((*auth_user_request)->username()) {
                 if (!request->auth_user_request) {
                     request->auth_user_request = *auth_user_request;
- AUTHUSERREQUESTLOCK(request->auth_user_request, "request");
                 }
             }
 
@@ -492,41 +440,36 @@
 
     /* copy username to request for logging on client-side */
     /* the credentials are correct at this point */
- if (NULL == request->auth_user_request) {
+ if (request->auth_user_request == NULL) {
         request->auth_user_request = *auth_user_request;
- AUTHUSERREQUESTLOCK(request->auth_user_request, "request");
         authenticateAuthUserRequestSetIp(*auth_user_request, src_addr);
     }
 
     return AUTH_AUTHENTICATED;
 }
 
-auth_acl_t
-
-AuthUserRequest::tryToAuthenticateAndSetAuthUser(AuthUserRequest ** auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
+AuthAclState
+AuthUserRequest::tryToAuthenticateAndSetAuthUser(AuthUserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr)
 {
     /* If we have already been called, return the cached value */
- AuthUserRequest *t = authTryGetUser (auth_user_request, conn, request);
+ AuthUserRequest::Pointer t = authTryGetUser(*auth_user_request, conn, request);
 
- if (t && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE
- && t->lastReply != AUTH_ACL_HELPER) {
- if (!*auth_user_request)
+ if (t != NULL && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE && t->lastReply != AUTH_ACL_HELPER) {
+ if (*auth_user_request == NULL)
             *auth_user_request = t;
 
- if (!request->auth_user_request && t->lastReply == AUTH_AUTHENTICATED) {
+ if (request->auth_user_request == NULL && t->lastReply == AUTH_AUTHENTICATED) {
             request->auth_user_request = t;
- AUTHUSERREQUESTLOCK(request->auth_user_request, "request");
         }
         return t->lastReply;
     }
 
     /* ok, call the actual authenticator routine. */
- auth_acl_t result = authenticate(auth_user_request, headertype, request, conn, src_addr);
-
- t = authTryGetUser (auth_user_request, conn, request);
-
- if (t && result != AUTH_ACL_CANNOT_AUTHENTICATE &&
- result != AUTH_ACL_HELPER)
+ AuthAclState result = authenticate(auth_user_request, headertype, request, conn, src_addr);
+
+ t = authTryGetUser(*auth_user_request, conn, request);
+
+ if (t != NULL && result != AUTH_ACL_CANNOT_AUTHENTICATE && result != AUTH_ACL_HELPER)
         t->lastReply = result;
 
     return result;
@@ -539,16 +482,16 @@
  * -2: authenticate broken in some fashion
  */
 int
-authenticateDirection(AuthUserRequest * auth_user_request)
+authenticateDirection(AuthUserRequest::Pointer auth_user_request)
 {
- if (!auth_user_request)
+ if (auth_user_request == NULL || auth_user_request->user() == NULL)
         return -2;
 
     return auth_user_request->direction();
 }
 
 void
-AuthUserRequest::addReplyAuthHeader(HttpReply * rep, AuthUserRequest * auth_user_request, HttpRequest * request, int accelerated, int internal)
+AuthUserRequest::addReplyAuthHeader(HttpReply * rep, AuthUserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal)
 /* send the auth types we are configured to support (and have compiled in!) */
 {
     http_hdr_type type;
@@ -585,7 +528,7 @@
         else {
             /* call each configured & running authscheme */
 
- for (authConfig::iterator i = Config.authConfiguration.begin(); i != Config.authConfiguration.end(); ++i) {
+ for (Auth::authConfig::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i) {
                 AuthConfig *scheme = *i;
 
                 if (scheme->active())
@@ -609,7 +552,7 @@
 }
 
 void
-authenticateFixHeader(HttpReply * rep, AuthUserRequest * auth_user_request, HttpRequest * request, int accelerated, int internal)
+authenticateFixHeader(HttpReply * rep, AuthUserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal)
 {
     AuthUserRequest::addReplyAuthHeader(rep, auth_user_request, request, accelerated, internal);
 }
@@ -617,41 +560,13 @@
 
 /* call the active auth module and allow it to add a trailer to the request */
 void
-authenticateAddTrailer(HttpReply * rep, AuthUserRequest * auth_user_request, HttpRequest * request, int accelerated)
+authenticateAddTrailer(HttpReply * rep, AuthUserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated)
 {
     if (auth_user_request != NULL)
         auth_user_request->addTrailer(rep, accelerated);
 }
 
-void
-
-AuthUserRequest::_lock()
-{
- assert(this);
- debugs(29, 9, "AuthUserRequest::lock: auth_user request '" << this << " " << references << "->" << references+1);
- ++references;
-}
-
-void
-AuthUserRequest::_unlock()
-{
- assert(this != NULL);
-
- if (references > 0) {
- debugs(29, 9, "AuthUserRequest::unlock: auth_user request '" << this << " " << references << "->" << references-1);
- --references;
- } else {
- debugs(29, 1, "Attempt to lower Auth User request " << this << " refcount below 0!");
- }
-
- if (references == 0) {
- debugs(29, 9, "AuthUserRequest::unlock: deleting auth_user_request '" << this << "'.");
- /* not locked anymore */
- delete this;
- }
-}
-
-AuthScheme *
+AuthScheme::Pointer
 AuthUserRequest::scheme() const
 {
     /* TODO: this should be overriden by the child and be essentially a no-op */

=== modified file 'src/auth/UserRequest.h'
--- src/auth/UserRequest.h 2010-04-17 02:29:04 +0000
+++ src/auth/UserRequest.h 2010-05-23 11:59:28 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -36,15 +35,22 @@
 #ifndef SQUID_AUTHUSERREQUEST_H
 #define SQUID_AUTHUSERREQUEST_H
 
-#include "client_side.h"
-
-class AuthUser;
+#include "auth/AuthAclState.h"
+#include "auth/Scheme.h"
+#include "auth/User.h"
+#include "dlink.h"
+#include "ip/Address.h"
+#include "typedefs.h"
+#include "HttpHeader.h"
 
 class ConnStateData;
-
-class AuthScheme;
-
-struct AuthUserIP {
+class HttpReply;
+class HttpRequest;
+
+/// \ingroup AuthAPI
+class AuthUserIP
+{
+public:
     dlink_node node;
     /* IP addr this user authenticated from */
 
@@ -55,9 +61,14 @@
 /**
  \ingroup AuthAPI
  * This is a short lived structure is the visible aspect of the authentication framework.
+ *
+ * It and its children hold the state data while processing authentication for a client request.
+ * The AuthenticationStateData object is merely a CBDATA wrapper for one of these.
  */
-class AuthUserRequest
+class AuthUserRequest : public RefCountable
 {
+public:
+ typedef RefCount<AuthUserRequest> Pointer;
 
 public:
     /**
@@ -65,7 +76,7 @@
      * it has request specific data, and links to user specific data
      * the user
      */
- AuthUser *_auth_user;
+ AuthUser::Pointer _auth_user;
 
     /**
      * Used by squid to determine what the next step in performing authentication for a given scheme is.
@@ -86,6 +97,19 @@
      \retval false Timeouts on cached credentials have occurred or for any reason the credentials are not valid.
      */
     virtual int authenticated() const = 0;
+
+ /**
+ * Check a auth_user pointer for validity.
+ * Does not check passwords, just data sensability. Broken or Unknown auth_types are not valid for use...
+ *
+ * \retval false User credentials are missing.
+ * \retval false User credentials use an unknown scheme type.
+ * \retval false User credentials are broken for their scheme.
+ *
+ * \retval true User credentials exist and may be able to authenticate.
+ */
+ bool valid() const;
+
     virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type) = 0;
     /* template method */
     virtual int module_direction() = 0;
@@ -102,14 +126,14 @@
      */
     virtual void module_start(RH *handler, void *data) = 0;
 
- virtual AuthUser *user() {return _auth_user;}
-
- virtual const AuthUser *user() const {return _auth_user;}
-
- virtual void user(AuthUser *aUser) {_auth_user=aUser;}
-
- static auth_acl_t tryToAuthenticateAndSetAuthUser(AuthUserRequest **, http_hdr_type, HttpRequest *, ConnStateData *, Ip::Address &);
- static void addReplyAuthHeader(HttpReply * rep, AuthUserRequest * auth_user_request, HttpRequest * request, int accelerated, int internal);
+ virtual AuthUser::Pointer user() {return _auth_user;}
+
+ virtual const AuthUser::Pointer user() const {return _auth_user;}
+
+ virtual void user(AuthUser::Pointer aUser) {_auth_user=aUser;}
+
+ static AuthAclState tryToAuthenticateAndSetAuthUser(AuthUserRequest::Pointer *, http_hdr_type, HttpRequest *, ConnStateData *, Ip::Address &);
+ static void addReplyAuthHeader(HttpReply * rep, AuthUserRequest::Pointer auth_user_request, HttpRequest * request, int accelerated, int internal);
 
     AuthUserRequest();
 
@@ -126,10 +150,6 @@
     /** Possibly overrideable in future */
     char const * getDenyMessage();
 
- size_t refCount() const;
- void _lock(); /**< \note please use AUTHUSERREQUESTLOCK() */
- void _unlock(); /**< \note please use AUTHUSERREQUESTUNLOCK() */
-
     /**
      * Squid does not make assumptions about where the username is stored.
      * This function must return a pointer to a NULL terminated string to be used in logging the request.
@@ -140,64 +160,45 @@
      */
     char const *username() const;
 
- AuthScheme *scheme() const;
+ AuthScheme::Pointer scheme() const;
 
     virtual const char * connLastHeader();
 
 private:
 
- static auth_acl_t authenticate(AuthUserRequest ** auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr);
+ static AuthAclState authenticate(AuthUserRequest::Pointer * auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData * conn, Ip::Address &src_addr);
 
     /** return a message on the 407 error pages */
     char *message;
 
- /** how many 'processes' are working on this data */
- size_t references;
-
     /**
      * We only attempt authentication once per http request. This
      * is to allow multiple auth acl references from different _access areas
      * when using connection based authentication
      */
- auth_acl_t lastReply;
+ AuthAclState lastReply;
 };
 
 /* AuthUserRequest */
 
-/**
- \ingroup AuthAPI
- \deprecated Use AuthUserRequest::refCount() instead.
- */
-extern size_t authenticateRequestRefCount (AuthUserRequest *);
-
-/// \ingroup AuthAPI
-extern void authenticateFixHeader(HttpReply *, AuthUserRequest *, HttpRequest *, int, int);
-/// \ingroup AuthAPI
-extern void authenticateAddTrailer(HttpReply *, AuthUserRequest *, HttpRequest *, int);
-
-/// \ingroup AuthAPI
-extern void authenticateAuthUserRequestRemoveIp(AuthUserRequest *, Ip::Address const &);
-/// \ingroup AuthAPI
-extern void authenticateAuthUserRequestClearIp(AuthUserRequest *);
-/// \ingroup AuthAPI
-extern int authenticateAuthUserRequestIPCount(AuthUserRequest *);
+/// \ingroup AuthAPI
+extern void authenticateFixHeader(HttpReply *, AuthUserRequest::Pointer, HttpRequest *, int, int);
+/// \ingroup AuthAPI
+extern void authenticateAddTrailer(HttpReply *, AuthUserRequest::Pointer, HttpRequest *, int);
+
+/// \ingroup AuthAPI
+extern void authenticateAuthUserRequestRemoveIp(AuthUserRequest::Pointer, Ip::Address const &);
+/// \ingroup AuthAPI
+extern void authenticateAuthUserRequestClearIp(AuthUserRequest::Pointer);
+/// \ingroup AuthAPI
+extern int authenticateAuthUserRequestIPCount(AuthUserRequest::Pointer);
 /// \ingroup AuthAPI
 /// \deprecated Use AuthUserRequest::direction() instead.
-extern int authenticateDirection(AuthUserRequest *);
+extern int authenticateDirection(AuthUserRequest::Pointer);
 
 /// \ingroup AuthAPI
 /// See AuthUserRequest::authenticated()
-extern int authenticateUserAuthenticated(AuthUserRequest *);
-/// \ingroup AuthAPI
-extern int authenticateValidateUser(AuthUserRequest *);
-
-/// \todo Drop dead code? or make a debugging option.
-#if 0
-#define AUTHUSERREQUESTUNLOCK(a,b) if(a){(a)->_unlock();debugs(0,0,HERE << "auth_user_request " << a << " was unlocked for " << b); (a)=NULL;}
-#define AUTHUSERREQUESTLOCK(a,b) { (a)->_lock(); debugs(0,0,HERE << "auth_user_request " << a << " was locked for " << b); }
-#endif
-#define AUTHUSERREQUESTUNLOCK(a,b) if(a){(a)->_unlock();(a)=NULL;}
-#define AUTHUSERREQUESTLOCK(a,b) (a)->_lock()
+extern int authenticateUserAuthenticated(AuthUserRequest::Pointer);
 
 
 #endif /* SQUID_AUTHUSERREQUEST_H */

=== modified file 'src/auth/basic/auth_basic.cc'
--- src/auth/basic/auth_basic.cc 2010-02-13 09:16:30 +0000
+++ src/auth/basic/auth_basic.cc 2010-05-23 12:40:04 +0000
@@ -38,31 +38,24 @@
 
 
 #include "squid.h"
-#include "auth_basic.h"
+#include "auth/basic/auth_basic.h"
+#include "auth/basic/basicScheme.h"
+#include "auth/basic/basicUserRequest.h"
 #include "auth/Gadgets.h"
+#include "auth/State.h"
 #include "CacheManager.h"
 #include "Store.h"
 #include "HttpReply.h"
-#include "basicScheme.h"
 #include "rfc1738.h"
 #include "wordlist.h"
 #include "SquidTime.h"
 
-static void
-authenticateStateFree(AuthenticateStateData * r)
-{
- cbdataFree(r);
-}
-
 /* Basic Scheme */
-
 static HLPCB authenticateBasicHandleReply;
 static AUTHSSTATS authenticateBasicStats;
 
 static helper *basicauthenticators = NULL;
 
-static AuthBasicConfig basicConfig;
-
 static int authbasic_initialised = 0;
 
 
@@ -74,27 +67,6 @@
 
 /* internal functions */
 
-/* TODO: move to basicScheme.cc - after all per request and user functions are moved out */
-void
-basicScheme::done()
-{
- /* TODO: this should be a Config call. */
-
- if (basicauthenticators)
- helperShutdown(basicauthenticators);
-
- authbasic_initialised = 0;
-
- if (!shutting_down)
- return;
-
- delete basicauthenticators;
- basicauthenticators = NULL;
-
- /* XXX Reinstate auth shutdown for dynamic schemes? */
- debugs(29, DBG_CRITICAL, HERE << "Basic authentication Shutdown.");
-}
-
 bool
 AuthBasicConfig::active() const
 {
@@ -117,20 +89,25 @@
 const char *
 AuthBasicConfig::type() const
 {
- return basicScheme::GetInstance().type();
-}
-
-AuthBasicUserRequest::AuthBasicUserRequest() : _theUser(NULL)
-{}
-
-AuthBasicUserRequest::~AuthBasicUserRequest()
-{}
-
+ return basicScheme::GetInstance()->type();
+}
+
+int32_t
+BasicUser::ttl() const
+{
+ if (credentials() != Ok && credentials() != Pending)
+ return -1; // TTL is obsolete NOW.
+
+ int32_t basic_ttl = expiretime - squid_curtime + static_cast<AuthBasicConfig*>(config)->credentialsTTL;
+ int32_t global_ttl = static_cast<int32_t>(expiretime - squid_curtime + Config.authenticateTTL);
+
+ return min(basic_ttl, global_ttl);
+}
 
 bool
 BasicUser::authenticated() const
 {
- if ((flags.credentials_ok == 1) && (credentials_checkedtime + basicConfig.credentialsTTL > squid_curtime))
+ if ((credentials() == Ok) && (expiretime + static_cast<AuthBasicConfig*>(config)->credentialsTTL > squid_curtime))
         return true;
 
     debugs(29, 4, "User not authenticated or credentials need rechecking.");
@@ -138,78 +115,8 @@
     return false;
 }
 
-int
-AuthBasicUserRequest::authenticated() const
-{
- BasicUser const *basic_auth = dynamic_cast<BasicUser const *>(user());
-
- if (basic_auth && basic_auth->authenticated())
- return 1;
-
- return 0;
-}
-
-/* log a basic user in
- */
-void
-AuthBasicUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
-{
- assert(user() != NULL);
-
- basic_data *basic_auth = dynamic_cast<BasicUser *>(user());
-
- /* if the password is not ok, do an identity */
-
- if (!basic_auth || basic_auth->flags.credentials_ok != 1)
- return;
-
- /* are we about to recheck the credentials externally? */
- if ((basic_auth->credentials_checkedtime + basicConfig.credentialsTTL) <= squid_curtime) {
- debugs(29, 4, "authBasicAuthenticate: credentials expired - rechecking");
- return;
- }
-
- /* we have been through the external helper, and the credentials haven't expired */
- debugs(29, 9, "authenticateBasicAuthenticateuser: user '" << basic_auth->username() << "' authenticated");
-
- /* Decode now takes care of finding the AuthUser struct in the cache */
- /* after external auth occurs anyway */
- basic_auth->expiretime = current_time.tv_sec;
-
- return;
-}
-
-int
-AuthBasicUserRequest::module_direction()
-{
- /* null auth_user is checked for by authenticateDirection */
- basic_data *basic_auth = dynamic_cast<BasicUser *>(user());
- assert (basic_auth);
-
- switch (basic_auth->flags.credentials_ok) {
-
- case 0: /* not checked */
- return -1;
-
- case 1: /* checked & ok */
-
- if (basic_auth->credentials_checkedtime + basicConfig.credentialsTTL <= squid_curtime)
- return -1;
-
- return 0;
-
- case 2: /* paused while waiting for a username:password check on another request */
- return -1;
-
- case 3: /* authentication process failed. */
- return 0;
- }
-
- return -2;
-}
-
-void
-AuthBasicConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
+void
+AuthBasicConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
 {
     if (authenticate) {
         debugs(29, 9, HERE << "Sending type:" << hdrType << " header: 'Basic realm=\"" << basicAuthRealm << "\"'");
@@ -217,10 +124,30 @@
     }
 }
 
-/* free any allocated configuration details */
+void
+AuthBasicConfig::rotateHelpers()
+{
+ /* schedule closure of existing helpers */
+ if (basicauthenticators) {
+ helperShutdown(basicauthenticators);
+ }
+
+ /* NP: dynamic helper restart will ensure they start up again as needed. */
+}
+
+/** shutdown the auth helpers and free any allocated configuration details */
 void
 AuthBasicConfig::done()
 {
+ authbasic_initialised = 0;
+
+ if (basicauthenticators) {
+ helperShutdown(basicauthenticators);
+ }
+
+ delete basicauthenticators;
+ basicauthenticators = NULL;
+
     if (authenticate)
         wordlistDestroy(&authenticate);
 
@@ -237,7 +164,7 @@
 static void
 authenticateBasicHandleReply(void *data, char *reply)
 {
- AuthenticateStateData *r = static_cast<AuthenticateStateData *>(data);
+ authenticateStateData *r = static_cast<authenticateStateData *>(data);
     BasicAuthQueueNode *tmpnode;
     char *t = NULL;
     void *cbdata;
@@ -253,20 +180,23 @@
 
     assert(r->auth_user_request != NULL);
     assert(r->auth_user_request->user()->auth_type == AUTH_BASIC);
- basic_data *basic_auth = dynamic_cast<basic_data *>(r->auth_user_request->user());
+
+ /* this is okay since we only play with the BasicUser child fields below
+ * and dont pass the pointer itself anywhere */
+ BasicUser *basic_auth = dynamic_cast<BasicUser *>(r->auth_user_request->user().getRaw());
 
     assert(basic_auth != NULL);
 
     if (reply && (strncasecmp(reply, "OK", 2) == 0))
- basic_auth->flags.credentials_ok = 1;
+ basic_auth->credentials(AuthUser::Ok);
     else {
- basic_auth->flags.credentials_ok = 3;
+ basic_auth->credentials(AuthUser::Failed);
 
         if (t && *t)
             r->auth_user_request->setDenyMessage(t);
     }
 
- basic_auth->credentials_checkedtime = squid_curtime;
+ basic_auth->expiretime = squid_curtime;
 
     if (cbdataReferenceValidDone(r->data, &cbdata))
         r->handler(cbdata, NULL);
@@ -306,10 +236,11 @@
     storeAppendPrintf(entry, "%s basic casesensitive %s\n", name, casesensitive ? "on" : "off");
 }
 
-AuthBasicConfig::AuthBasicConfig() : authenticateChildren(20)
+AuthBasicConfig::AuthBasicConfig() :
+ credentialsTTL( 2*60*60 ),
+ casesensitive(0),
+ utf8(0)
 {
- /* TODO: move into initialisation list */
- credentialsTTL = 2 * 60 * 60; /* two hours */
     basicAuthRealm = xstrdup("Squid proxy-caching web server");
 }
 
@@ -349,9 +280,7 @@
     helperStats(sentry, basicauthenticators, "Basic Authenticator Statistics");
 }
 
-CBDATA_TYPE(AuthenticateStateData);
-
-static AuthUser *
+static AuthUser::Pointer
 authBasicAuthUserFindUsername(const char *username)
 {
     AuthUserHashPointer *usernamehash;
@@ -376,10 +305,14 @@
     delete this;
 }
 
-BasicUser::BasicUser(AuthConfig *aConfig) : AuthUser (aConfig) , passwd (NULL), credentials_checkedtime(0), auth_queue(NULL), cleartext (NULL), currentRequest (NULL), httpAuthHeader (NULL)
-{
- flags.credentials_ok = 0;
-}
+BasicUser::BasicUser(AuthConfig *aConfig) :
+ AuthUser(aConfig),
+ passwd(NULL),
+ auth_queue(NULL),
+ cleartext(NULL),
+ currentRequest(NULL),
+ httpAuthHeader(NULL)
+{}
 
 bool
 BasicUser::decodeCleartext()
@@ -430,7 +363,7 @@
         *seperator = ':';
     }
 
- if (!basicConfig.casesensitive)
+ if (!static_cast<AuthBasicConfig*>(config)->casesensitive)
         Tolower((char *)username());
 }
 
@@ -456,15 +389,18 @@
 }
 
 void
-BasicUser::decode(char const *proxy_auth, AuthUserRequest *auth_user_request)
+BasicUser::decode(char const *proxy_auth, AuthUserRequest::Pointer auth_user_request)
 {
     currentRequest = auth_user_request;
     httpAuthHeader = proxy_auth;
- if (decodeCleartext ()) {
+ if (decodeCleartext()) {
         extractUsername();
         extractPassword();
     }
- currentRequest = NULL;
+ currentRequest = NULL; // AYJ: why ?? we have only just filled it with data!
+ // so that we dont have circular UserRequest->User->UseRequest loops persisting outside the auth decode sequence????
+
+ // okay we dont need the original buffer string any more.
     httpAuthHeader = NULL;
 }
 
@@ -478,64 +414,44 @@
     return true;
 }
 
+/**
+ * Generate a duplicate of the bad credentials before clearing the working copy.
+ */
 void
-BasicUser::makeLoggingInstance(AuthBasicUserRequest *auth_user_request)
+BasicUser::makeLoggingInstance(AuthUserRequest::Pointer auth_user_request)
 {
     if (username()) {
         /* log the username */
         debugs(29, 9, HERE << "Creating new user for logging '" << username() << "'");
         /* new scheme data */
- BasicUser *basic_auth = new BasicUser(& basicConfig);
+ AuthUser::Pointer basic_auth = dynamic_cast<AuthUser*>(new BasicUser(config));
         auth_user_request->user(basic_auth);
         /* save the credentials */
         basic_auth->username(username());
         username(NULL);
         /* set the auth_user type */
         basic_auth->auth_type = AUTH_BROKEN;
- /* link the request to the user */
- basic_auth->addRequest(auth_user_request);
     }
 }
 
-AuthUser *
-BasicUser::makeCachedFrom()
-{
- /* the user doesn't exist in the username cache yet */
- debugs(29, 9, HERE << "Creating new user '" << username() << "'");
- BasicUser *basic_user = new BasicUser(&basicConfig);
- /* save the credentials */
- basic_user->username(username());
- username(NULL);
- basic_user->passwd = passwd;
- passwd = NULL;
- /* set the auth_user type */
- basic_user->auth_type = AUTH_BASIC;
- /* current time for timeouts */
- basic_user->expiretime = current_time.tv_sec;
-
- /* this basic_user struct is the 'lucky one' to get added to the username cache */
- /* the requests after this link to the basic_user */
- /* store user in hash */
- basic_user->addToNameCache();
- return basic_user;
-}
-
 void
 BasicUser::updateCached(BasicUser *from)
 {
- debugs(29, 9, HERE << "Found user '" << from->username() << "' in the user cache as '" << this << "'");
+ debugs(29, 9, HERE << "Found user '" << from->username() << "' already in the user cache as '" << this << "'");
+
+ assert(strcmp(from->username(), username()) == 0);
 
     if (strcmp(from->passwd, passwd)) {
         debugs(29, 4, HERE << "new password found. Updating in user master record and resetting auth state to unchecked");
- flags.credentials_ok = 0;
+ credentials(Unchecked);
         xfree(passwd);
         passwd = from->passwd;
         from->passwd = NULL;
     }
 
- if (flags.credentials_ok == 3) {
+ if (credentials() == Failed) {
         debugs(29, 4, HERE << "last attempt to authenticate this user failed, resetting auth state to unchecked");
- flags.credentials_ok = 0;
+ credentials(Unchecked);
     }
 }
 
@@ -546,56 +462,66 @@
  * "cannot decode credentials". Use the message field to return a
  * descriptive message to the user.
  */
-AuthUserRequest *
+AuthUserRequest::Pointer
 AuthBasicConfig::decode(char const *proxy_auth)
 {
- AuthBasicUserRequest *auth_user_request = new AuthBasicUserRequest();
+ AuthUserRequest::Pointer auth_user_request = dynamic_cast<AuthUserRequest*>(new AuthBasicUserRequest);
     /* decode the username */
     /* trim BASIC from string */
 
     while (xisgraph(*proxy_auth))
         proxy_auth++;
 
- BasicUser *basic_auth, local_basic(&basicConfig);
+ /* decoder copy. maybe temporary. maybe added to hash if none already existing. */
+ BasicUser *local_basic = new BasicUser(this);
 
     /* Trim leading whitespace before decoding */
     while (xisspace(*proxy_auth))
         proxy_auth++;
 
- local_basic.decode(proxy_auth, auth_user_request);
+ local_basic->decode(proxy_auth, auth_user_request);
 
- if (!local_basic.valid()) {
- local_basic.makeLoggingInstance(auth_user_request);
+ if (!local_basic->valid()) {
+ local_basic->makeLoggingInstance(auth_user_request);
         return auth_user_request;
     }
 
- /* now lookup and see if we have a matching auth_user structure in
- * memory. */
-
- AuthUser *auth_user;
-
- if ((auth_user = authBasicAuthUserFindUsername(local_basic.username())) == NULL) {
- auth_user = local_basic.makeCachedFrom();
- basic_auth = dynamic_cast<BasicUser *>(auth_user);
- assert (basic_auth);
+ /* now lookup and see if we have a matching auth_user structure in memory. */
+ AuthUser::Pointer auth_user;
+
+ if ((auth_user = authBasicAuthUserFindUsername(local_basic->username())) == NULL) {
+ /* the user doesn't exist in the username cache yet */
+ /* save the credentials */
+ debugs(29, 9, HERE << "Creating new user '" << local_basic->username() << "'");
+ /* set the auth_user type */
+ local_basic->auth_type = AUTH_BASIC;
+ /* current time for timeouts */
+ local_basic->expiretime = current_time.tv_sec;
+
+ /* this basic_user struct is the 'lucky one' to get added to the username cache */
+ /* the requests after this link to the basic_user */
+ /* store user in hash */
+ local_basic->addToNameCache();
+
+ auth_user = dynamic_cast<AuthUser*>(local_basic);
+ assert(auth_user != NULL);
     } else {
- basic_auth = dynamic_cast<BasicUser *>(auth_user);
- assert (basic_auth);
- basic_auth->updateCached (&local_basic);
+ /* replace the current cached password with the new one */
+ BasicUser *basic_auth = dynamic_cast<BasicUser *>(auth_user.getRaw());
+ assert(basic_auth);
+ basic_auth->updateCached(local_basic);
+ auth_user = basic_auth;
     }
 
     /* link the request to the in-cache user */
- auth_user_request->user(basic_auth);
-
- basic_auth->addRequest(auth_user_request);
-
+ auth_user_request->user(auth_user);
     return auth_user_request;
 }
 
 /** Initialize helpers and the like for this auth scheme. Called AFTER parsing the
  * config file */
 void
-AuthBasicConfig::init(AuthConfig * scheme)
+AuthBasicConfig::init(AuthConfig * schemeCfg)
 {
     if (authenticate) {
         authbasic_initialised = 1;
@@ -611,7 +537,7 @@
 
         helperOpenServers(basicauthenticators);
 
- CBDATA_INIT_TYPE(AuthenticateStateData);
+ CBDATA_INIT_TYPE(authenticateStateData);
     }
 }
 
@@ -625,10 +551,10 @@
 }
 
 void
-BasicUser::queueRequest(AuthUserRequest * auth_user_request, RH * handler, void *data)
+BasicUser::queueRequest(AuthUserRequest::Pointer auth_user_request, RH * handler, void *data)
 {
     BasicAuthQueueNode *node;
- node = static_cast<BasicAuthQueueNode *>(xmalloc(sizeof(BasicAuthQueueNode)));
+ node = static_cast<BasicAuthQueueNode *>(xcalloc(1, sizeof(BasicAuthQueueNode)));
     assert(node);
     /* save the details */
     node->next = auth_queue;
@@ -638,44 +564,19 @@
     node->data = cbdataReference(data);
 }
 
-/* send the initial data to a basic authenticator module */
-void
-AuthBasicUserRequest::module_start(RH * handler, void *data)
-{
- basic_data *basic_auth;
- assert(user()->auth_type == AUTH_BASIC);
- basic_auth = dynamic_cast<basic_data *>(user());
- assert(basic_auth != NULL);
- debugs(29, 9, HERE << "'" << basic_auth->username() << ":" << basic_auth->passwd << "'");
-
- if (basicConfig.authenticate == NULL) {
- handler(data, NULL);
- return;
- }
-
- /* check to see if the auth_user already has a request outstanding */
- if (basic_auth->flags.credentials_ok == 2) {
- /* there is a request with the same credentials already being verified */
- basic_auth->queueRequest(this, handler, data);
- return;
- }
-
- basic_auth->submitRequest (this, handler, data);
-}
-
-void
-BasicUser::submitRequest(AuthUserRequest * auth_user_request, RH * handler, void *data)
-{
- /* mark the user as haveing verification in progress */
- flags.credentials_ok = 2;
- AuthenticateStateData *r = NULL;
+void
+BasicUser::submitRequest(AuthUserRequest::Pointer auth_user_request, RH * handler, void *data)
+{
+ /* mark the user as having verification in progress */
+ credentials(Pending);
+ authenticateStateData *r = NULL;
     char buf[8192];
     char user[1024], pass[1024];
- r = cbdataAlloc(AuthenticateStateData);
+ r = cbdataAlloc(authenticateStateData);
     r->handler = handler;
     r->data = cbdataReference(data);
     r->auth_user_request = auth_user_request;
- if (basicConfig.utf8) {
+ if (static_cast<AuthBasicConfig*>(config)->utf8) {
         latin1_to_utf8(user, sizeof(user), username());
         latin1_to_utf8(pass, sizeof(pass), passwd);
         xstrncpy(user, rfc1738_escape(user), sizeof(user));
@@ -687,9 +588,3 @@
     snprintf(buf, sizeof(buf), "%s %s\n", user, pass);
     helperSubmit(basicauthenticators, buf, authenticateBasicHandleReply, r);
 }
-
-AuthConfig *
-basicScheme::createConfig()
-{
- return &basicConfig;
-}

=== modified file 'src/auth/basic/auth_basic.h'
--- src/auth/basic/auth_basic.h 2009-12-19 05:47:00 +0000
+++ src/auth/basic/auth_basic.h 2010-05-06 11:07:19 +0000
@@ -5,6 +5,7 @@
 
 #ifndef __AUTH_BASIC_H__
 #define __AUTH_BASIC_H__
+
 #include "auth/Gadgets.h"
 #include "auth/User.h"
 #include "auth/UserRequest.h"
@@ -13,31 +14,17 @@
 
 #define DefaultAuthenticateChildrenMax 32 /* 32 processes */
 
-/* Generic */
-
-class AuthenticateStateData
-{
-
-public:
- void *data;
- AuthUserRequest *auth_user_request;
- RH *handler;
-};
-
-/* queue of auth requests waiting for verification to occur */
-
+/** queue of auth requests waiting for verification to occur */
 class BasicAuthQueueNode
 {
 
 public:
     BasicAuthQueueNode *next;
- AuthUserRequest *auth_user_request;
+ AuthUserRequest::Pointer auth_user_request;
     RH *handler;
     void *data;
 };
 
-class AuthBasicUserRequest;
-
 class BasicUser : public AuthUser
 {
 
@@ -48,23 +35,20 @@
     BasicUser(AuthConfig *);
     ~BasicUser();
     bool authenticated() const;
- void queueRequest(AuthUserRequest * auth_user_request, RH * handler, void *data);
- void submitRequest (AuthUserRequest * auth_user_request, RH * handler, void *data);
- void decode(char const *credentials, AuthUserRequest *);
+ void queueRequest(AuthUserRequest::Pointer auth_user_request, RH * handler, void *data);
+ void submitRequest(AuthUserRequest::Pointer auth_user_request, RH * handler, void *data);
+ void decode(char const *credentials, AuthUserRequest::Pointer);
     char *getCleartext() {return cleartext;}
 
     bool valid() const;
- void makeLoggingInstance(AuthBasicUserRequest *auth_user_request);
- AuthUser * makeCachedFrom();
+ void makeLoggingInstance(AuthUserRequest::Pointer auth_user_request);
+
+ /** Update the cached password for a username. */
     void updateCached(BasicUser *from);
+ virtual int32_t ttl() const;
+
     char *passwd;
- time_t credentials_checkedtime;
-
- struct {
-
-unsigned int credentials_ok:
- 2; /*0=unchecked,1=ok,2=failed */
- } flags;
+
     BasicAuthQueueNode *auth_queue;
 
 private:
@@ -72,43 +56,12 @@
     void extractUsername();
     void extractPassword();
     char *cleartext;
- AuthUserRequest *currentRequest;
+ AuthUserRequest::Pointer currentRequest;
     char const *httpAuthHeader;
 };
 
 MEMPROXY_CLASS_INLINE(BasicUser);
 
-typedef class BasicUser basic_data;
-
-/* follows the http request around */
-
-class AuthBasicUserRequest : public AuthUserRequest
-{
-
-public:
- MEMPROXY_CLASS(AuthBasicUserRequest);
-
- AuthBasicUserRequest();
- virtual ~AuthBasicUserRequest();
-
- virtual int authenticated() const;
- virtual void authenticate(HttpRequest * request, ConnStateData *conn, http_hdr_type type);
- virtual int module_direction();
- virtual void module_start(RH *, void *);
- virtual AuthUser *user() {return _theUser;}
-
- virtual const AuthUser *user() const {return _theUser;}
-
- virtual void user (AuthUser *aUser) {_theUser=dynamic_cast<BasicUser *>(aUser);}
-
-private:
- BasicUser *_theUser;
-};
-
-MEMPROXY_CLASS_INLINE(AuthBasicUserRequest);
-
-#include "HelperChildConfig.h"
-
 /* configuration runtime data */
 
 class AuthBasicConfig : public AuthConfig
@@ -119,20 +72,19 @@
     ~AuthBasicConfig();
     virtual bool active() const;
     virtual bool configured() const;
- virtual AuthUserRequest *decode(char const *proxy_auth);
+ virtual AuthUserRequest::Pointer decode(char const *proxy_auth);
     virtual void done();
+ virtual void rotateHelpers();
     virtual void dump(StoreEntry *, const char *, AuthConfig *);
- virtual void fixHeader(AuthUserRequest *, HttpReply *, http_hdr_type, HttpRequest *);
+ virtual void fixHeader(AuthUserRequest::Pointer, HttpReply *, http_hdr_type, HttpRequest *);
     virtual void init(AuthConfig *);
     virtual void parse(AuthConfig *, int, char *);
     virtual void registerWithCacheManager(void);
     virtual const char * type() const;
- HelperChildConfig authenticateChildren;
     char *basicAuthRealm;
- wordlist *authenticate;
     time_t credentialsTTL;
     int casesensitive;
     int utf8;
 };
 
-#endif
+#endif /* __AUTH_BASIC_H__ */

=== modified file 'src/auth/basic/basicScheme.cc'
--- src/auth/basic/basicScheme.cc 2009-05-20 06:59:04 +0000
+++ src/auth/basic/basicScheme.cc 2010-01-17 11:58:04 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -31,19 +30,23 @@
  *
  */
 
-#include "basicScheme.h"
-
-AuthScheme &
+#include "config.h"
+#include "auth/basic/basicScheme.h"
+#include "helper.h"
+
+/* for AuthConfig */
+#include "auth/basic/auth_basic.h"
+
+AuthScheme::Pointer basicScheme::_instance = NULL;
+
+AuthScheme::Pointer
 basicScheme::GetInstance()
 {
- if (_instance == NULL)
+ if (_instance == NULL) {
         _instance = new basicScheme();
- return *_instance;
-}
-
-basicScheme::basicScheme()
-{
- AddScheme(*this);
+ AddScheme(_instance);
+ }
+ return _instance;
 }
 
 char const *
@@ -52,4 +55,18 @@
     return "basic";
 }
 
-basicScheme *basicScheme::_instance = NULL;
+void
+basicScheme::done()
+{
+ /* clear the global handle to this scheme. */
+ _instance = NULL;
+
+ debugs(29, DBG_CRITICAL, HERE << "Basic authentication Schema Detached.");
+}
+
+AuthConfig *
+basicScheme::createConfig()
+{
+ AuthBasicConfig *newCfg = new AuthBasicConfig;
+ return dynamic_cast<AuthConfig*>(newCfg);
+}

=== modified file 'src/auth/basic/basicScheme.h'
--- src/auth/basic/basicScheme.h 2009-05-20 06:59:04 +0000
+++ src/auth/basic/basicScheme.h 2010-01-17 11:58:04 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -35,6 +34,7 @@
 #define SQUID_BASICSCHEME_H
 
 #include "auth/Scheme.h"
+#include "auth/basic/auth_basic.h"
 
 /// \ingroup AuthAPI
 /// \ingroup AuthSchemeAPI
@@ -42,8 +42,8 @@
 {
 
 public:
- static AuthScheme &GetInstance();
- basicScheme();
+ static AuthScheme::Pointer GetInstance();
+ basicScheme() {};
     virtual ~basicScheme() {}
 
     /* per scheme */
@@ -55,7 +55,8 @@
     basicScheme &operator=(basicScheme const &);
 
 private:
- static basicScheme *_instance;
+ static AuthScheme::Pointer _instance;
+// AuthBasicConfig basicConfig;
 };
 
 #endif /* SQUID_BASICSCHEME_H */

=== added file 'src/auth/basic/basicUserRequest.cc'
--- src/auth/basic/basicUserRequest.cc 1970-01-01 00:00:00 +0000
+++ src/auth/basic/basicUserRequest.cc 2010-05-06 11:07:19 +0000
@@ -0,0 +1,95 @@
+#include "config.h"
+#include "auth/basic/basicUserRequest.h"
+#include "SquidTime.h"
+
+#include "auth/basic/auth_basic.h"
+
+int
+AuthBasicUserRequest::authenticated() const
+{
+ BasicUser const *basic_auth = dynamic_cast<BasicUser const *>(user().getRaw());
+
+ if (basic_auth && basic_auth->authenticated())
+ return 1;
+
+ return 0;
+}
+
+/* log a basic user in
+ */
+void
+AuthBasicUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
+{
+ assert(user() != NULL);
+
+ /* if the password is not ok, do an identity */
+ if (!user() || user()->credentials() != AuthUser::Ok)
+ return;
+
+ /* are we about to recheck the credentials externally? */
+ if ((user()->expiretime + static_cast<AuthBasicConfig*>(AuthConfig::Find("basic"))->credentialsTTL) <= squid_curtime) {
+ debugs(29, 4, HERE << "credentials expired - rechecking");
+ return;
+ }
+
+ /* we have been through the external helper, and the credentials haven't expired */
+ debugs(29, 9, HERE << "user '" << user()->username() << "' authenticated");
+
+ /* Decode now takes care of finding the AuthUser struct in the cache */
+ /* after external auth occurs anyway */
+ user()->expiretime = current_time.tv_sec;
+
+ return;
+}
+
+int
+AuthBasicUserRequest::module_direction()
+{
+ /* null auth_user is checked for by authenticateDirection */
+ if (user()->auth_type != AUTH_BASIC)
+ return -2;
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Unchecked:
+ case AuthUser::Pending:
+ return -1;
+
+ case AuthUser::Ok:
+ if (user()->expiretime + static_cast<AuthBasicConfig*>(AuthConfig::Find("basic"))->credentialsTTL <= squid_curtime)
+ return -1;
+ return 0;
+
+ case AuthUser::Failed:
+ return 0;
+
+ default:
+ return -2;
+ }
+}
+
+/* send the initial data to a basic authenticator module */
+void
+AuthBasicUserRequest::module_start(RH * handler, void *data)
+{
+ assert(user()->auth_type == AUTH_BASIC);
+ BasicUser *basic_auth = dynamic_cast<BasicUser *>(user().getRaw());
+ assert(basic_auth != NULL);
+ debugs(29, 9, HERE << "'" << basic_auth->username() << ":" << basic_auth->passwd << "'");
+
+ if (static_cast<AuthBasicConfig*>(AuthConfig::Find("basic"))->authenticate == NULL) {
+ debugs(29, DBG_CRITICAL, "ERROR: No Basic authentication program configured.");
+ handler(data, NULL);
+ return;
+ }
+
+ /* check to see if the auth_user already has a request outstanding */
+ if (user()->credentials() == AuthUser::Pending) {
+ /* there is a request with the same credentials already being verified */
+ basic_auth->queueRequest(this, handler, data);
+ return;
+ }
+
+ basic_auth->submitRequest(this, handler, data);
+}
+

=== added file 'src/auth/basic/basicUserRequest.h'
--- src/auth/basic/basicUserRequest.h 1970-01-01 00:00:00 +0000
+++ src/auth/basic/basicUserRequest.h 2010-04-08 11:53:16 +0000
@@ -0,0 +1,29 @@
+#ifndef _SQUID_SRC_AUTH_BASIC_USERREQUEST_H
+#define _SQUID_SRC_AUTH_BASIC_USERREQUEST_H
+
+#include "MemPool.h"
+#include "auth/UserRequest.h"
+
+class ConnStateData;
+class HttpRequest;
+
+/* follows the http request around */
+
+class AuthBasicUserRequest : public AuthUserRequest
+{
+
+public:
+ MEMPROXY_CLASS(AuthBasicUserRequest);
+
+ AuthBasicUserRequest() {};
+ virtual ~AuthBasicUserRequest() { assert(RefCountCount()==0); };
+
+ virtual int authenticated() const;
+ virtual void authenticate(HttpRequest * request, ConnStateData *conn, http_hdr_type type);
+ virtual int module_direction();
+ virtual void module_start(RH *, void *);
+};
+
+MEMPROXY_CLASS_INLINE(AuthBasicUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_BASIC_USERREQUEST_H */

=== modified file 'src/auth/digest/auth_digest.cc'
--- src/auth/digest/auth_digest.cc 2010-04-17 02:29:04 +0000
+++ src/auth/digest/auth_digest.cc 2010-05-06 11:07:19 +0000
@@ -39,7 +39,7 @@
 
 #include "squid.h"
 #include "rfc2617.h"
-#include "auth_digest.h"
+#include "auth/digest/auth_digest.h"
 #include "auth/Gadgets.h"
 #include "event.h"
 #include "CacheManager.h"
@@ -49,23 +49,21 @@
 #include "wordlist.h"
 #include "SquidTime.h"
 /* TODO don't include this */
-#include "digestScheme.h"
+#include "auth/digest/digestScheme.h"
+#include "auth/digest/digestUserRequest.h"
 
 /* Digest Scheme */
 
-static HLPCB authenticateDigestHandleReply;
 static AUTHSSTATS authenticateDigestStats;
 
-static helper *digestauthenticators = NULL;
+helper *digestauthenticators = NULL;
 
 static hash_table *digest_nonce_cache;
 
-static AuthDigestConfig digestConfig;
-
 static int authdigest_initialised = 0;
 static MemAllocator *digest_nonce_pool = NULL;
 
-CBDATA_TYPE(DigestAuthenticateStateData);
+// CBDATA_TYPE(DigestAuthenticateStateData);
 
 enum http_digest_attr_type {
     DIGEST_USERNAME,
@@ -107,13 +105,9 @@
 static void authenticateDigestNonceSetup(void);
 static void authenticateDigestNonceShutdown(void);
 static void authenticateDigestNonceReconfigure(void);
-static const char *authenticateDigestNonceNonceb64(digest_nonce_h * nonce);
-static int authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9]);
 static int authDigestNonceIsStale(digest_nonce_h * nonce);
 static void authDigestNonceEncode(digest_nonce_h * nonce);
-static int authDigestNonceLastRequest(digest_nonce_h * nonce);
 static void authDigestNonceLink(digest_nonce_h * nonce);
-static void authDigestNonceUnlink(digest_nonce_h * nonce);
 #if NOT_USED
 static int authDigestNonceLinks(digest_nonce_h * nonce);
 #endif
@@ -233,7 +227,7 @@
     if (!digest_nonce_cache) {
         digest_nonce_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
         assert(digest_nonce_cache);
- eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, digestConfig.nonceGCInterval, 1);
+ eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->nonceGCInterval, 1);
     }
 }
 
@@ -300,8 +294,8 @@
 
     debugs(29, 3, "authenticateDigestNonceCacheCleanup: Finished cleaning the nonce cache.");
 
- if (digestConfig.active())
- eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, digestConfig.nonceGCInterval, 1);
+ if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->active())
+ eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->nonceGCInterval, 1);
 }
 
 static void
@@ -324,7 +318,7 @@
 
 #endif
 
-static void
+void
 authDigestNonceUnlink(digest_nonce_h * nonce)
 {
     assert(nonce != NULL);
@@ -341,8 +335,8 @@
         authenticateDigestNonceDelete(nonce);
 }
 
-static const char *
-authenticateDigestNonceNonceb64(digest_nonce_h * nonce)
+const char *
+authenticateDigestNonceNonceb64(const digest_nonce_h * nonce)
 {
     if (!nonce)
         return NULL;
@@ -370,7 +364,7 @@
     return nonce;
 }
 
-static int
+int
 authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9])
 {
     unsigned long intnc;
@@ -388,12 +382,12 @@
     }
 
     /* is the nonce-count ok ? */
- if (!digestConfig.CheckNonceCount) {
+ if (!static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->CheckNonceCount) {
         nonce->nc++;
         return -1; /* forced OK by configuration */
     }
 
- if ((digestConfig.NonceStrictness && intnc != nonce->nc + 1) ||
+ if ((static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->NonceStrictness && intnc != nonce->nc + 1) ||
             intnc < nonce->nc + 1) {
         debugs(29, 4, "authDigestNonceIsValid: Nonce count doesn't match");
         nonce->flags.valid = 0;
@@ -418,10 +412,10 @@
         return -1;
 
     /* has it's max duration expired? */
- if (nonce->noncedata.creationtime + digestConfig.noncemaxduration < current_time.tv_sec) {
+ if (nonce->noncedata.creationtime + static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxduration < current_time.tv_sec) {
         debugs(29, 4, "authDigestNonceIsStale: Nonce is too old. " <<
                nonce->noncedata.creationtime << " " <<
- digestConfig.noncemaxduration << " " <<
+ static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxduration << " " <<
                current_time.tv_sec);
 
         nonce->flags.valid = 0;
@@ -434,7 +428,7 @@
         return -1;
     }
 
- if (nonce->nc > digestConfig.noncemaxuses) {
+ if (nonce->nc > static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxuses) {
         debugs(29, 4, "authDigestNoncelastRequest: Nonce count over user limit");
         nonce->flags.valid = 0;
         return -1;
@@ -444,8 +438,11 @@
     return 0;
 }
 
-/* return -1 if the digest will be stale on the next request */
-static int
+/**
+ * \retval 0 the digest is not stale yet
+ * \retval -1 the digest will be stale on the next request
+ */
+const int
 authDigestNonceLastRequest(digest_nonce_h * nonce)
 {
     if (!nonce)
@@ -456,7 +453,7 @@
         return -1;
     }
 
- if (nonce->nc >= digestConfig.noncemaxuses - 1) {
+ if (nonce->nc >= static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxuses - 1) {
         debugs(29, 4, "authDigestNoncelastRequest: Nonce count about to hit user limit");
         return -1;
     }
@@ -483,46 +480,36 @@
 }
 
 /* USER related functions */
-static AuthUser *
+static AuthUser::Pointer
 authDigestUserFindUsername(const char *username)
 {
     AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
     debugs(29, 9, HERE << "Looking for user '" << username << "'");
 
     if (username && (usernamehash = static_cast < auth_user_hash_pointer * >(hash_lookup(proxy_auth_username_cache, username)))) {
- while ((usernamehash->user()->auth_type != AUTH_DIGEST) &&
- (usernamehash->next))
- usernamehash = static_cast < auth_user_hash_pointer * >(usernamehash->next);
-
- auth_user = NULL;
+ while ((usernamehash->user()->auth_type != AUTH_DIGEST) && (usernamehash->next))
+ usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
 
         if (usernamehash->user()->auth_type == AUTH_DIGEST) {
- auth_user = usernamehash->user();
+ return usernamehash->user();
         }
-
- return auth_user;
     }
 
     return NULL;
 }
 
-static void
-authDigestUserShutdown(void)
+void
+AuthDigestConfig::rotateHelpers()
 {
- /** \todo Future work: the auth framework could flush it's cache */
- AuthUserHashPointer *usernamehash;
- AuthUser *auth_user;
- hash_first(proxy_auth_username_cache);
-
- while ((usernamehash = ((auth_user_hash_pointer *) hash_next(proxy_auth_username_cache)))) {
- auth_user = usernamehash->user();
-
- if (strcmp(auth_user->config->type(), "digest") == 0)
- auth_user->unlock();
+ /* schedule closure of existing helpers */
+ if (digestauthenticators) {
+ helperShutdown(digestauthenticators);
     }
+
+ /* NP: dynamic helper restart will ensure they start up again as needed. */
 }
 
+
 /** delete the digest request structure. Does NOT delete related structures */
 void
 digestScheme::done()
@@ -547,9 +534,12 @@
     delete digestauthenticators;
     digestauthenticators = NULL;
 
- authDigestUserShutdown();
+ PurgeCredentialsCache();
     authenticateDigestNonceShutdown();
     debugs(29, 2, "authenticateDigestDone: Digest authentication shut down.");
+
+ /* clear the global handle to this scheme. */
+ _instance = NULL;
 }
 
 void
@@ -589,231 +579,18 @@
     return false;
 }
 
-int
-AuthDigestUserRequest::authenticated() const
-{
- if (credentials() == Ok)
- return 1;
-
- return 0;
-}
-
-/** log a digest user in
- */
-void
-AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
-{
- AuthUser *auth_user;
- AuthDigestUserRequest *digest_request;
- digest_user_h *digest_user;
-
- HASHHEX SESSIONKEY;
- HASHHEX HA2 = "";
- HASHHEX Response;
-
- assert(authUser() != NULL);
- auth_user = authUser();
-
- digest_user = dynamic_cast < digest_user_h * >(auth_user);
-
- assert(digest_user != NULL);
-
- /* if the check has corrupted the user, just return */
-
- if (credentials() == Failed) {
- return;
- }
-
- digest_request = this;
-
- /* do we have the HA1 */
-
- if (!digest_user->HA1created) {
- credentials(Pending);
- return;
- }
-
- if (digest_request->nonce == NULL) {
- /* this isn't a nonce we issued */
- credentials(Failed);
- return;
- }
-
- DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL,
- authenticateDigestNonceNonceb64(digest_request->nonce),
- digest_request->cnonce,
- digest_user->HA1, SESSIONKEY);
- DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
- digest_request->nc, digest_request->cnonce, digest_request->qop,
- RequestMethodStr(request->method), digest_request->uri, HA2, Response);
-
- debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'");
-
- if (strcasecmp(digest_request->response, Response) != 0) {
- if (!digest_request->flags.helper_queried) {
- /* Query the helper in case the password has changed */
- digest_request->flags.helper_queried = 1;
- digest_request->credentials_ok = Pending;
- return;
- }
-
- if (digestConfig.PostWorkaround && request->method != METHOD_GET) {
- /* Ugly workaround for certain very broken browsers using the
- * wrong method to calculate the request-digest on POST request.
- * This should be deleted once Digest authentication becomes more
- * widespread and such broken browsers no longer are commonly
- * used.
- */
- DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
- digest_request->nc, digest_request->cnonce, digest_request->qop,
- RequestMethodStr(METHOD_GET), digest_request->uri, HA2, Response);
-
- if (strcasecmp(digest_request->response, Response)) {
- credentials(Failed);
- digest_request->flags.invalid_password = 1;
- digest_request->setDenyMessage("Incorrect password");
- return;
- } else {
- const char *useragent = request->header.getStr(HDR_USER_AGENT);
-
- static Ip::Address last_broken_addr;
- static int seen_broken_client = 0;
-
- if (!seen_broken_client) {
- last_broken_addr.SetNoAddr();
- seen_broken_client = 1;
- }
-
- if (last_broken_addr != request->client_addr) {
- debugs(29, 1, "\nDigest POST bug detected from " <<
- request->client_addr << " using '" <<
- (useragent ? useragent : "-") <<
- "'. Please upgrade browser. See Bug #630 for details.");
-
- last_broken_addr = request->client_addr;
- }
- }
- } else {
- credentials(Failed);
- digest_request->flags.invalid_password = 1;
- digest_request->setDenyMessage("Incorrect password");
- return;
- }
-
- /* check for stale nonce */
- if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) {
- debugs(29, 3, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK but nonce stale");
- credentials(Failed);
- digest_request->setDenyMessage("Stale nonce");
- return;
- }
- }
-
- credentials(Ok);
-
- /* password was checked and did match */
- debugs(29, 4, "authenticateDigestAuthenticateuser: user '" << digest_user->username() << "' validated OK");
-
- /* auth_user is now linked, we reset these values
- * after external auth occurs anyway */
- auth_user->expiretime = current_time.tv_sec;
- return;
-}
-
-int
-AuthDigestUserRequest::module_direction()
-{
- switch (credentials()) {
-
- case Unchecked:
- return -1;
-
- case Ok:
-
- return 0;
-
- case Pending:
- return -1;
-
- case Failed:
-
- /* send new challenge */
- return 1;
- }
-
- return -2;
-}
-
-/* add the [proxy]authorisation header */
-void
-AuthDigestUserRequest::addHeader(HttpReply * rep, int accel)
-{
- http_hdr_type type;
-
- /* don't add to authentication error pages */
-
- if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
- || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
- return;
-
- type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
-
-#if WAITING_FOR_TE
- /* test for http/1.1 transfer chunked encoding */
- if (chunkedtest)
- return;
-
-#endif
-
- if ((digestConfig.authenticate) && authDigestNonceLastRequest(nonce)) {
- flags.authinfo_sent = 1;
- debugs(29, 9, "authDigestAddHead: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
- httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
- }
-}
-
-#if WAITING_FOR_TE
-/* add the [proxy]authorisation header */
-void
-AuthDigestUserRequest::addTrailer(HttpReply * rep, int accel)
-{
- int type;
-
- if (!auth_user_request)
- return;
-
-
- /* has the header already been send? */
- if (flags.authinfo_sent)
- return;
-
- /* don't add to authentication error pages */
- if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
- || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
- return;
-
- type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
-
- if ((digestConfig.authenticate) && authDigestNonceLastRequest(nonce)) {
- debugs(29, 9, "authDigestAddTrailer: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
- httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
- }
-}
-
-#endif
-
 /* add the [www-|Proxy-]authenticate header on a 407 or 401 reply */
 void
-AuthDigestConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
+AuthDigestConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
 {
     if (!authenticate)
         return;
 
     int stale = 0;
 
- if (auth_user_request) {
+ if (auth_user_request != NULL) {
         AuthDigestUserRequest *digest_request;
- digest_request = dynamic_cast < AuthDigestUserRequest * >(auth_user_request);
+ digest_request = dynamic_cast<AuthDigestUserRequest*>(auth_user_request.getRaw());
         assert (digest_request != NULL);
 
         stale = !digest_request->flags.invalid_password;
@@ -833,7 +610,6 @@
 
 DigestUser::~DigestUser()
 {
-
     dlink_node *link, *tmplink;
     link = nonces.head;
 
@@ -847,53 +623,30 @@
     }
 }
 
-static void
-authenticateDigestHandleReply(void *data, char *reply)
+int32_t
+DigestUser::ttl() const
 {
- DigestAuthenticateStateData *replyData = static_cast < DigestAuthenticateStateData * >(data);
- AuthUserRequest *auth_user_request;
- AuthDigestUserRequest *digest_request;
- digest_user_h *digest_user;
- char *t = NULL;
- void *cbdata;
- debugs(29, 9, "authenticateDigestHandleReply: {" << (reply ? reply : "<NULL>") << "}");
-
- if (reply) {
- if ((t = strchr(reply, ' ')))
- *t++ = '\0';
-
- if (*reply == '\0' || *reply == '\n')
- reply = NULL;
- }
-
- assert(replyData->auth_user_request != NULL);
- auth_user_request = replyData->auth_user_request;
- digest_request = dynamic_cast < AuthDigestUserRequest * >(auth_user_request);
- assert(digest_request);
-
- digest_user = dynamic_cast < digest_user_h * >(auth_user_request->user());
- assert(digest_user != NULL);
-
- if (reply && (strncasecmp(reply, "ERR", 3) == 0)) {
- digest_request->credentials(AuthDigestUserRequest::Failed);
- digest_request->flags.invalid_password = 1;
-
- if (t && *t)
- digest_request->setDenyMessage(t);
- } else if (reply) {
- CvtBin(reply, digest_user->HA1);
- digest_user->HA1created = 1;
- }
-
- if (cbdataReferenceValidDone(replyData->data, &cbdata))
- replyData->handler(cbdata, NULL);
-
- //we know replyData->auth_user_request != NULL, or we'd have asserted
- AUTHUSERREQUESTUNLOCK(replyData->auth_user_request, "replyData");
-
- cbdataFree(replyData);
+ int32_t global_ttl = static_cast<int32_t>(expiretime - squid_curtime + Config.authenticateTTL);
+
+ /* find the longest lasting nonce. */
+ int32_t latest_nonce = -1;
+ dlink_node *link = nonces.head;
+ while (link) {
+ digest_nonce_h *nonce = static_cast<digest_nonce_h *>(link->data);
+ if (nonce->flags.valid && nonce->noncedata.creationtime > latest_nonce)
+ latest_nonce = nonce->noncedata.creationtime;
+
+ link = link->next;
+ }
+ if (latest_nonce == -1)
+ return min(-1, global_ttl);
+
+ int32_t nonce_ttl = latest_nonce - current_time.tv_sec + static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxduration;
+
+ return min(nonce_ttl, global_ttl);
 }
 
+
 /* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
  * config file */
 void
@@ -915,7 +668,7 @@
 
         helperOpenServers(digestauthenticators);
 
- CBDATA_INIT_TYPE(DigestAuthenticateStateData);
+ CBDATA_INIT_TYPE(authenticateStateData);
     }
 }
 
@@ -938,7 +691,7 @@
     safe_free(digestAuthRealm);
 }
 
-AuthDigestConfig::AuthDigestConfig() : authenticateChildren(20)
+AuthDigestConfig::AuthDigestConfig()
 {
     /* TODO: move into initialisation list */
     /* 5 minutes */
@@ -989,7 +742,7 @@
 const char *
 AuthDigestConfig::type() const
 {
- return digestScheme::GetInstance().type();
+ return digestScheme::GetInstance()->type();
 }
 
 
@@ -1004,7 +757,7 @@
 static void
 authDigestNonceUserUnlink(digest_nonce_h * nonce)
 {
- digest_user_h *digest_user;
+ DigestUser *digest_user;
     dlink_node *link, *tmplink;
 
     if (!nonce)
@@ -1044,7 +797,7 @@
 authDigestUserLinkNonce(DigestUser * user, digest_nonce_h * nonce)
 {
     dlink_node *node;
- digest_user_h *digest_user;
+ DigestUser *digest_user;
 
     if (!user || !nonce)
         return;
@@ -1075,22 +828,20 @@
 }
 
 /* setup the necessary info to log the username */
-static AuthUserRequest *
-authDigestLogUsername(char *username, AuthDigestUserRequest *auth_user_request)
+static AuthUserRequest::Pointer
+authDigestLogUsername(char *username, AuthUserRequest::Pointer auth_user_request)
 {
     assert(auth_user_request != NULL);
 
     /* log the username */
     debugs(29, 9, "authDigestLogUsername: Creating new user for logging '" << username << "'");
- digest_user_h *digest_user = new DigestUser(&digestConfig);
+ AuthUser::Pointer digest_user = new DigestUser(static_cast<AuthDigestConfig*>(AuthConfig::Find("digest")));
     /* save the credentials */
     digest_user->username(username);
     /* set the auth_user type */
     digest_user->auth_type = AUTH_BROKEN;
     /* link the request to the user */
- auth_user_request->authUser(digest_user);
     auth_user_request->user(digest_user);
- digest_user->addRequest (auth_user_request);
     return auth_user_request;
 }
 
@@ -1098,7 +849,7 @@
  * Decode a Digest [Proxy-]Auth string, placing the results in the passed
  * Auth_user structure.
  */
-AuthUserRequest *
+AuthUserRequest::Pointer
 AuthDigestConfig::decode(char const *proxy_auth)
 {
     const char *item;
@@ -1318,7 +1069,7 @@
     if (!nonce) {
         /* we couldn't find a matching nonce! */
         debugs(29, 2, "authenticateDigestDecode: Unexpected or invalid nonce received");
- digest_request->credentials(AuthDigestUserRequest::Failed);
+ digest_request->user()->credentials(AuthUser::Failed);
         return authDigestLogUsername(username, digest_request);
     }
 
@@ -1337,14 +1088,14 @@
     /* we don't send or parse opaques. Ok so we're flexable ... */
 
     /* find the user */
- digest_user_h *digest_user;
+ DigestUser *digest_user;
 
- AuthUser *auth_user;
+ AuthUser::Pointer auth_user;
 
     if ((auth_user = authDigestUserFindUsername(username)) == NULL) {
         /* the user doesn't exist in the username cache yet */
         debugs(29, 9, "authDigestDecodeAuth: Creating new digest user '" << username << "'");
- digest_user = new DigestUser (&digestConfig);
+ digest_user = new DigestUser(this);
         /* auth_user is a parent */
         auth_user = digest_user;
         /* save the username */
@@ -1364,19 +1115,14 @@
         authDigestUserLinkNonce(digest_user, nonce);
     } else {
         debugs(29, 9, "authDigestDecodeAuth: Found user '" << username << "' in the user cache as '" << auth_user << "'");
- digest_user = static_cast < digest_user_h * >(auth_user);
+ digest_user = static_cast<DigestUser *>(auth_user.getRaw());
         xfree(username);
     }
 
     /*link the request and the user */
     assert(digest_request != NULL);
 
- digest_request->authUser (digest_user);
-
     digest_request->user(digest_user);
-
- digest_user->addRequest (digest_request);
-
     debugs(29, 9, "username = '" << digest_user->username() << "'\nrealm = '" <<
            digest_request->realm << "'\nqop = '" << digest_request->qop <<
            "'\nalgorithm = '" << digest_request->algorithm << "'\nuri = '" <<
@@ -1388,95 +1134,5 @@
     return digest_request;
 }
 
-/* send the initial data to a digest authenticator module */
-void
-AuthDigestUserRequest::module_start(RH * handler, void *data)
-{
- DigestAuthenticateStateData *r = NULL;
- char buf[8192];
- digest_user_h *digest_user;
- assert(user()->auth_type == AUTH_DIGEST);
- digest_user = dynamic_cast < digest_user_h * >(user());
- assert(digest_user != NULL);
- debugs(29, 9, "authenticateStart: '\"" << digest_user->username() << "\":\"" << realm << "\"'");
-
- if (digestConfig.authenticate == NULL) {
- handler(data, NULL);
- return;
- }
-
- r = cbdataAlloc(DigestAuthenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = this;
- AUTHUSERREQUESTLOCK(r->auth_user_request, "r");
- if (digestConfig.utf8) {
- char userstr[1024];
- latin1_to_utf8(userstr, sizeof(userstr), digest_user->username());
- snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm);
- } else {
- snprintf(buf, 8192, "\"%s\":\"%s\"\n", digest_user->username(), realm);
- }
-
- helperSubmit(digestauthenticators, buf, authenticateDigestHandleReply, r);
-}
-
-DigestUser::DigestUser (AuthConfig *aConfig) : AuthUser (aConfig), HA1created (0)
-{}
-
-AuthUser *
-AuthDigestUserRequest::authUser() const
-{
- return const_cast<AuthUser *>(user());
-}
-
-void
-AuthDigestUserRequest::authUser(AuthUser *aUser)
-{
- assert(!authUser());
- user(aUser);
- user()->lock();
-}
-
-AuthDigestUserRequest::CredentialsState
-AuthDigestUserRequest::credentials() const
-{
- return credentials_ok;
-}
-
-void
-AuthDigestUserRequest::credentials(CredentialsState newCreds)
-{
- credentials_ok = newCreds;
-}
-
-AuthDigestUserRequest::AuthDigestUserRequest() : nonceb64(NULL) ,cnonce(NULL) ,realm(NULL),
- pszPass(NULL) ,algorithm(NULL) ,pszMethod(NULL),
- qop(NULL) ,uri(NULL) ,response(NULL),
- nonce(NULL), _theUser (NULL) ,
- credentials_ok (Unchecked)
-{}
-
-/** delete the digest request structure. Does NOT delete related structures */
-AuthDigestUserRequest::~AuthDigestUserRequest()
-{
- safe_free (nonceb64);
- safe_free (cnonce);
- safe_free (realm);
- safe_free (pszPass);
- safe_free (algorithm);
- safe_free (pszMethod);
- safe_free (qop);
- safe_free (uri);
- safe_free (response);
-
- if (nonce)
- authDigestNonceUnlink(nonce);
-}
-
-AuthConfig *
-digestScheme::createConfig()
-{
- return &digestConfig;
-}
-
+DigestUser::DigestUser(AuthConfig *aConfig) : AuthUser(aConfig), HA1created (0)
+{}

=== modified file 'src/auth/digest/auth_digest.h'
--- src/auth/digest/auth_digest.h 2009-12-16 03:46:59 +0000
+++ src/auth/digest/auth_digest.h 2010-04-26 07:07:44 +0000
@@ -5,24 +5,17 @@
 
 #ifndef __AUTH_DIGEST_H__
 #define __AUTH_DIGEST_H__
-#include "rfc2617.h"
+
+#include "auth/Config.h"
 #include "auth/Gadgets.h"
+#include "auth/State.h"
 #include "auth/User.h"
 #include "auth/UserRequest.h"
-#include "auth/Config.h"
 #include "helper.h"
+#include "rfc2617.h"
 
 /* Generic */
 
-class DigestAuthenticateStateData
-{
-
-public:
- void *data;
- AuthUserRequest *auth_user_request;
- RH *handler;
-};
-
 typedef struct _digest_nonce_data digest_nonce_data;
 
 typedef struct _digest_nonce_h digest_nonce_h;
@@ -36,6 +29,9 @@
     DigestUser(AuthConfig *);
     ~DigestUser();
     int authenticated() const;
+
+ virtual int32_t ttl() const;
+
     HASH HA1;
     int HA1created;
 
@@ -46,66 +42,6 @@
 
 MEMPROXY_CLASS_INLINE(DigestUser);
 
-typedef class DigestUser digest_user_h;
-
-/* the digest_request structure is what follows the http_request around */
-
-class AuthDigestUserRequest : public AuthUserRequest
-{
-
-public:
- enum CredentialsState {Unchecked, Ok, Pending, Failed};
- MEMPROXY_CLASS(AuthDigestUserRequest);
-
- AuthDigestUserRequest();
- virtual ~AuthDigestUserRequest();
-
- virtual int authenticated() const;
- virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
- virtual int module_direction();
- virtual void addHeader(HttpReply * rep, int accel);
-#if WAITING_FOR_TE
-
- virtual void addTrailer(HttpReply * rep, int accel);
-#endif
-
- virtual void module_start(RH *, void *);
- virtual AuthUser *user() {return _theUser;}
-
- virtual const AuthUser *user() const {return _theUser;}
-
- virtual void user(AuthUser *aUser) {_theUser=dynamic_cast<DigestUser *>(aUser);}
-
- CredentialsState credentials() const;
- void credentials(CredentialsState);
-
- void authUser(AuthUser *);
- AuthUser *authUser() const;
-
- char *nonceb64; /* "dcd98b7102dd2f0e8b11d0f600bfb0c093" */
- char *cnonce; /* "0a4f113b" */
- char *realm; /* = "testrealm_at_host.com" */
- char *pszPass; /* = "Circle Of Life" */
- char *algorithm; /* = "md5" */
- char nc[9]; /* = "00000001" */
- char *pszMethod; /* = "GET" */
- char *qop; /* = "auth" */
- char *uri; /* = "/dir/index.html" */
- char *response;
-
- struct {
- unsigned int authinfo_sent:1;
- unsigned int invalid_password:1;
- unsigned int helper_queried:1;
- } flags;
- digest_nonce_h *nonce;
-
-private:
- DigestUser *_theUser;
- CredentialsState credentials_ok;
-};
-
-MEMPROXY_CLASS_INLINE(AuthDigestUserRequest);
 
 /* data to be encoded into the nonce's b64 representation */
 
@@ -134,7 +70,10 @@
     } flags;
 };
 
-#include "HelperChildConfig.h"
+extern void authDigestNonceUnlink(digest_nonce_h * nonce);
+extern int authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9]);
+extern const char *authenticateDigestNonceNonceb64(const digest_nonce_h * nonce);
+extern const int authDigestNonceLastRequest(digest_nonce_h * nonce);
 
 /* configuration runtime data */
 
@@ -145,17 +84,16 @@
     AuthDigestConfig();
     virtual bool active() const;
     virtual bool configured() const;
- virtual AuthUserRequest *decode(char const *proxy_auth);
+ virtual AuthUserRequest::Pointer decode(char const *proxy_auth);
     virtual void done();
+ virtual void rotateHelpers();
     virtual void dump(StoreEntry *, const char *, AuthConfig *);
- virtual void fixHeader(AuthUserRequest *, HttpReply *, http_hdr_type, HttpRequest *);
+ virtual void fixHeader(AuthUserRequest::Pointer, HttpReply *, http_hdr_type, HttpRequest *);
     virtual void init(AuthConfig *);
     virtual void parse(AuthConfig *, int, char *);
     virtual void registerWithCacheManager(void);
     virtual const char * type() const;
- HelperChildConfig authenticateChildren;
     char *digestAuthRealm;
- wordlist *authenticate;
     time_t nonceGCInterval;
     time_t noncemaxduration;
     unsigned int noncemaxuses;
@@ -170,4 +108,6 @@
 /* strings */
 #define QOP_AUTH "auth"
 
+extern helper *digestauthenticators;
+
 #endif

=== modified file 'src/auth/digest/digestScheme.cc'
--- src/auth/digest/digestScheme.cc 2009-05-20 06:59:04 +0000
+++ src/auth/digest/digestScheme.cc 2010-04-11 09:02:42 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -31,19 +30,18 @@
  *
  */
 
-#include "digestScheme.h"
+#include "config.h"
+#include "auth/digest/digestScheme.h"
+#include "helper.h"
 
-AuthScheme &
+AuthScheme::Pointer
 digestScheme::GetInstance()
 {
- if (_instance == NULL)
+ if (_instance == NULL) {
         _instance = new digestScheme();
- return *_instance;
-}
-
-digestScheme::digestScheme()
-{
- AddScheme(*this);
+ AddScheme(_instance);
+ }
+ return _instance;
 }
 
 char const *
@@ -52,4 +50,28 @@
     return "digest";
 }
 
-digestScheme *digestScheme::_instance = NULL;
+AuthScheme::Pointer digestScheme::_instance = NULL;
+
+AuthConfig *
+digestScheme::createConfig()
+{
+ AuthDigestConfig *digestCfg = new AuthDigestConfig;
+ return dynamic_cast<AuthConfig*>(digestCfg);
+}
+
+void
+digestScheme::PurgeCredentialsCache(void)
+{
+ AuthUserHashPointer *usernamehash;
+ AuthUser::Pointer auth_user;
+ hash_first(proxy_auth_username_cache);
+
+ while ((usernamehash = static_cast<AuthUserHashPointer *>(hash_next(proxy_auth_username_cache)) )) {
+ auth_user = usernamehash->user();
+
+ if (strcmp(auth_user->config->type(), "digest") == 0) {
+ hash_remove_link(proxy_auth_username_cache, static_cast<hash_link*>(usernamehash));
+ delete usernamehash;
+ }
+ }
+}

=== modified file 'src/auth/digest/digestScheme.h'
--- src/auth/digest/digestScheme.h 2009-05-20 06:59:04 +0000
+++ src/auth/digest/digestScheme.h 2010-04-11 09:02:42 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -35,6 +34,7 @@
 #define SQUID_DIGESTSCHEME_H
 
 #include "auth/Scheme.h"
+#include "auth/digest/auth_digest.h"
 
 /// \ingroup AuthSchemeAPI
 /// \ingroup AuthAPI
@@ -42,20 +42,28 @@
 {
 
 public:
- static AuthScheme &GetInstance();
- digestScheme();
+ static AuthScheme::Pointer GetInstance();
+ digestScheme() {};
     virtual ~digestScheme() {}
 
     /* per scheme */
     virtual char const *type () const;
     virtual void done();
     virtual AuthConfig *createConfig();
+
     /* Not implemented */
     digestScheme (digestScheme const &);
     digestScheme &operator=(digestScheme const &);
 
 private:
- static digestScheme *_instance;
+ static AuthScheme::Pointer _instance;
+
+ /**
+ * Remove all cached user credentials from circulation.
+ * Intended for use during shutdown procedure.
+ * After calling this all newly received credentials must be re-authenticated.
+ */
+ static void PurgeCredentialsCache(void);
 };
 
 #endif /* SQUID_DIGESTSCHEME_H */

=== added file 'src/auth/digest/digestUserRequest.cc'
--- src/auth/digest/digestUserRequest.cc 1970-01-01 00:00:00 +0000
+++ src/auth/digest/digestUserRequest.cc 2010-05-06 11:07:19 +0000
@@ -0,0 +1,321 @@
+#include "config.h"
+#include "auth/digest/auth_digest.h"
+#include "auth/digest/digestUserRequest.h"
+#include "auth/State.h"
+#include "HttpReply.h"
+#include "HttpRequest.h"
+#include "SquidTime.h"
+
+AuthDigestUserRequest::AuthDigestUserRequest() :
+ nonceb64(NULL),
+ cnonce(NULL),
+ realm(NULL),
+ pszPass(NULL),
+ algorithm(NULL),
+ pszMethod(NULL),
+ qop(NULL),
+ uri(NULL),
+ response(NULL),
+ nonce(NULL)
+{}
+
+/**
+ * Delete the digest request structure.
+ * Does NOT delete related AuthUser structures
+ */
+AuthDigestUserRequest::~AuthDigestUserRequest()
+{
+ assert(RefCountCount()==0);
+
+ safe_free(nonceb64);
+ safe_free(cnonce);
+ safe_free(realm);
+ safe_free(pszPass);
+ safe_free(algorithm);
+ safe_free(pszMethod);
+ safe_free(qop);
+ safe_free(uri);
+ safe_free(response);
+
+ if (nonce)
+ authDigestNonceUnlink(nonce);
+}
+
+int
+AuthDigestUserRequest::authenticated() const
+{
+ if (user() != NULL && user()->credentials() == AuthUser::Ok)
+ return 1;
+
+ return 0;
+}
+
+/** log a digest user in
+ */
+void
+AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
+{
+ HASHHEX SESSIONKEY;
+ HASHHEX HA2 = "";
+ HASHHEX Response;
+
+ /* if the check has corrupted the user, just return */
+ if (user() == NULL || user()->credentials() == AuthUser::Failed) {
+ return;
+ }
+
+ AuthUser::Pointer auth_user = user();
+
+ DigestUser *digest_user = dynamic_cast<DigestUser*>(auth_user.getRaw());
+ assert(digest_user != NULL);
+
+ AuthDigestUserRequest *digest_request = this;
+
+ /* do we have the HA1 */
+ if (!digest_user->HA1created) {
+ auth_user->credentials(AuthUser::Pending);
+ return;
+ }
+
+ if (digest_request->nonce == NULL) {
+ /* this isn't a nonce we issued */
+ auth_user->credentials(AuthUser::Failed);
+ return;
+ }
+
+ DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL,
+ authenticateDigestNonceNonceb64(digest_request->nonce),
+ digest_request->cnonce,
+ digest_user->HA1, SESSIONKEY);
+ DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
+ digest_request->nc, digest_request->cnonce, digest_request->qop,
+ RequestMethodStr(request->method), digest_request->uri, HA2, Response);
+
+ debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'");
+
+ if (strcasecmp(digest_request->response, Response) != 0) {
+ if (!digest_request->flags.helper_queried) {
+ /* Query the helper in case the password has changed */
+ digest_request->flags.helper_queried = 1;
+ auth_user->credentials(AuthUser::Pending);
+ return;
+ }
+
+ if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->PostWorkaround && request->method != METHOD_GET) {
+ /* Ugly workaround for certain very broken browsers using the
+ * wrong method to calculate the request-digest on POST request.
+ * This should be deleted once Digest authentication becomes more
+ * widespread and such broken browsers no longer are commonly
+ * used.
+ */
+ DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
+ digest_request->nc, digest_request->cnonce, digest_request->qop,
+ RequestMethodStr(METHOD_GET), digest_request->uri, HA2, Response);
+
+ if (strcasecmp(digest_request->response, Response)) {
+ auth_user->credentials(AuthUser::Failed);
+ digest_request->flags.invalid_password = 1;
+ digest_request->setDenyMessage("Incorrect password");
+ return;
+ } else {
+ const char *useragent = request->header.getStr(HDR_USER_AGENT);
+
+ static Ip::Address last_broken_addr;
+ static int seen_broken_client = 0;
+
+ if (!seen_broken_client) {
+ last_broken_addr.SetNoAddr();
+ seen_broken_client = 1;
+ }
+
+ if (last_broken_addr != request->client_addr) {
+ debugs(29, 1, "\nDigest POST bug detected from " <<
+ request->client_addr << " using '" <<
+ (useragent ? useragent : "-") <<
+ "'. Please upgrade browser. See Bug #630 for details.");
+
+ last_broken_addr = request->client_addr;
+ }
+ }
+ } else {
+ auth_user->credentials(AuthUser::Failed);
+ digest_request->flags.invalid_password = 1;
+ digest_request->setDenyMessage("Incorrect password");
+ return;
+ }
+
+ /* check for stale nonce */
+ if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) {
+ debugs(29, 3, "authenticateDigestAuthenticateuser: user '" << auth_user->username() << "' validated OK but nonce stale");
+ auth_user->credentials(AuthUser::Failed);
+ digest_request->setDenyMessage("Stale nonce");
+ return;
+ }
+ }
+
+ auth_user->credentials(AuthUser::Ok);
+
+ /* password was checked and did match */
+ debugs(29, 4, "authenticateDigestAuthenticateuser: user '" << auth_user->username() << "' validated OK");
+
+ /* auth_user is now linked, we reset these values
+ * after external auth occurs anyway */
+ auth_user->expiretime = current_time.tv_sec;
+ return;
+}
+
+int
+AuthDigestUserRequest::module_direction()
+{
+ if (user()->auth_type != AUTH_DIGEST)
+ return -2;
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Ok:
+ return 0;
+
+ case AuthUser::Failed:
+ /* send new challenge */
+ return 1;
+
+ case AuthUser::Unchecked:
+ case AuthUser::Pending:
+ return -1;
+
+ default:
+ return -2;
+ }
+}
+
+/* add the [proxy]authorisation header */
+void
+AuthDigestUserRequest::addHeader(HttpReply * rep, int accel)
+{
+ http_hdr_type type;
+
+ /* don't add to authentication error pages */
+
+ if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
+ || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
+ return;
+
+ type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
+
+#if WAITING_FOR_TE
+ /* test for http/1.1 transfer chunked encoding */
+ if (chunkedtest)
+ return;
+#endif
+
+ if ((static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->authenticate) && authDigestNonceLastRequest(nonce)) {
+ flags.authinfo_sent = 1;
+ debugs(29, 9, "authDigestAddHead: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
+ httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
+ }
+}
+
+#if WAITING_FOR_TE
+/** add the [proxy]authorisation header */
+void
+AuthDigestUserRequest::addTrailer(HttpReply * rep, int accel)
+{
+ int type;
+
+ if (!auth_user_request)
+ return;
+
+ /* has the header already been send? */
+ if (flags.authinfo_sent)
+ return;
+
+ /* don't add to authentication error pages */
+ if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
+ || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
+ return;
+
+ type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
+
+ if ((static_cast<AuthDigestConfig*>(digestScheme::GetInstance()->getConfig())->authenticate) && authDigestNonceLastRequest(nonce)) {
+ debugs(29, 9, "authDigestAddTrailer: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
+ httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
+ }
+}
+#endif
+
+/* send the initial data to a digest authenticator module */
+void
+AuthDigestUserRequest::module_start(RH * handler, void *data)
+{
+ authenticateStateData *r = NULL;
+ char buf[8192];
+
+ assert(user() != NULL && user()->auth_type == AUTH_DIGEST);
+ debugs(29, 9, "authenticateStart: '\"" << user()->username() << "\":\"" << realm << "\"'");
+
+ if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->authenticate == NULL) {
+ debugs(29, DBG_CRITICAL, "ERROR: No Digest authentication program configured.");
+ handler(data, NULL);
+ return;
+ }
+
+ r = cbdataAlloc(authenticateStateData);
+ r->handler = handler;
+ r->data = cbdataReference(data);
+ r->auth_user_request = static_cast<AuthUserRequest*>(this);
+ if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->utf8) {
+ char userstr[1024];
+ latin1_to_utf8(userstr, sizeof(userstr), user()->username());
+ snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm);
+ } else {
+ snprintf(buf, 8192, "\"%s\":\"%s\"\n", user()->username(), realm);
+ }
+
+ helperSubmit(digestauthenticators, buf, AuthDigestUserRequest::HandleReply, r);
+}
+
+void
+AuthDigestUserRequest::HandleReply(void *data, char *reply)
+{
+ authenticateStateData *replyData = static_cast < authenticateStateData * >(data);
+ char *t = NULL;
+ void *cbdata;
+ debugs(29, 9, HERE << "{" << (reply ? reply : "<NULL>") << "}");
+
+ if (reply) {
+ if ((t = strchr(reply, ' ')))
+ *t++ = '\0';
+
+ if (*reply == '\0' || *reply == '\n')
+ reply = NULL;
+ }
+
+ assert(replyData->auth_user_request != NULL);
+ AuthUserRequest::Pointer auth_user_request = replyData->auth_user_request;
+
+ if (reply && (strncasecmp(reply, "ERR", 3) == 0)) {
+ /* allow this because the digest_request pointer is purely local */
+ AuthDigestUserRequest *digest_request = dynamic_cast<AuthDigestUserRequest *>(auth_user_request.getRaw());
+ assert(digest_request);
+
+ digest_request->user()->credentials(AuthUser::Failed);
+ digest_request->flags.invalid_password = 1;
+
+ if (t && *t)
+ digest_request->setDenyMessage(t);
+ } else if (reply) {
+ /* allow this because the digest_request pointer is purely local */
+ DigestUser *digest_user = dynamic_cast<DigestUser *>(auth_user_request->user().getRaw());
+ assert(digest_user != NULL);
+
+ CvtBin(reply, digest_user->HA1);
+ digest_user->HA1created = 1;
+ }
+
+ if (cbdataReferenceValidDone(replyData->data, &cbdata))
+ replyData->handler(cbdata, NULL);
+
+ replyData->auth_user_request = NULL;
+
+ cbdataFree(replyData);
+}

=== added file 'src/auth/digest/digestUserRequest.h'
--- src/auth/digest/digestUserRequest.h 1970-01-01 00:00:00 +0000
+++ src/auth/digest/digestUserRequest.h 2010-05-06 11:07:19 +0000
@@ -0,0 +1,59 @@
+#ifndef _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H
+#define _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H
+
+#include "auth/UserRequest.h"
+#include "auth/digest/auth_digest.h"
+#include "MemPool.h"
+
+class ConnStateData;
+class HttpReply;
+class HttpRequest;
+
+/**
+ * The AuthDigestUserRequest structure is what follows the http_request around
+ */
+class AuthDigestUserRequest : public AuthUserRequest
+{
+
+public:
+ MEMPROXY_CLASS(AuthDigestUserRequest);
+
+ AuthDigestUserRequest();
+ virtual ~AuthDigestUserRequest();
+
+ virtual int authenticated() const;
+ virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
+ virtual int module_direction();
+ virtual void addHeader(HttpReply * rep, int accel);
+#if WAITING_FOR_TE
+
+ virtual void addTrailer(HttpReply * rep, int accel);
+#endif
+
+ virtual void module_start(RH *, void *);
+
+ char *nonceb64; /* "dcd98b7102dd2f0e8b11d0f600bfb0c093" */
+ char *cnonce; /* "0a4f113b" */
+ char *realm; /* = "testrealm_at_host.com" */
+ char *pszPass; /* = "Circle Of Life" */
+ char *algorithm; /* = "md5" */
+ char nc[9]; /* = "00000001" */
+ char *pszMethod; /* = "GET" */
+ char *qop; /* = "auth" */
+ char *uri; /* = "/dir/index.html" */
+ char *response;
+
+ struct {
+ unsigned int authinfo_sent:1;
+ unsigned int invalid_password:1;
+ unsigned int helper_queried:1;
+ } flags;
+ digest_nonce_h *nonce;
+
+private:
+ static HLPCB HandleReply;
+};
+
+MEMPROXY_CLASS_INLINE(AuthDigestUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_DIGEST_USERREQUEST_H */

=== modified file 'src/auth/negotiate/auth_negotiate.cc'
--- src/auth/negotiate/auth_negotiate.cc 2010-02-13 09:16:30 +0000
+++ src/auth/negotiate/auth_negotiate.cc 2010-05-06 11:07:19 +0000
@@ -38,8 +38,9 @@
 
 
 #include "squid.h"
-#include "auth_negotiate.h"
+#include "auth/negotiate/auth_negotiate.h"
 #include "auth/Gadgets.h"
+#include "auth/State.h"
 #include "CacheManager.h"
 #include "Store.h"
 #include "client_side.h"
@@ -47,7 +48,8 @@
 #include "HttpRequest.h"
 #include "SquidTime.h"
 /** \todo remove this include */
-#include "negotiateScheme.h"
+#include "auth/negotiate/negotiateScheme.h"
+#include "auth/negotiate/negotiateUserRequest.h"
 #include "wordlist.h"
 
 /**
@@ -55,35 +57,17 @@
  \ingroup AuthNegotiateAPI
  */
 
-/**
- * Maximum length (buffer size) for token strings.
- */
-// AYJ: must match re-definition in helpers/negotiate_auth/squid_kerb_auth/squid_kerb_auth.c
-#define MAX_AUTHTOKEN_LEN 32768
-
-
-/// \ingroup AuthNegotiateInternal
-static void
-authenticateStateFree(authenticateStateData * r)
-{
- AUTHUSERREQUESTUNLOCK(r->auth_user_request, "r");
- cbdataFree(r);
-}
-
 /* Negotiate Scheme */
-static HLPSCB authenticateNegotiateHandleReply;
 static AUTHSSTATS authenticateNegotiateStats;
 
 /// \ingroup AuthNegotiateInternal
-static statefulhelper *negotiateauthenticators = NULL;
-
-CBDATA_TYPE(authenticateStateData);
+statefulhelper *negotiateauthenticators = NULL;
 
 /// \ingroup AuthNegotiateInternal
 static int authnegotiate_initialised = 0;
 
 /// \ingroup AuthNegotiateInternal
-static auth_negotiate_config negotiateConfig;
+AuthNegotiateConfig negotiateConfig;
 
 /// \ingroup AuthNegotiateInternal
 static hash_table *proxy_auth_cache = NULL;
@@ -94,38 +78,39 @@
  *
  */
 
-/**
- \ingroup AuthNegotiateInternal
- \todo move to negotiateScheme.cc
- */
 void
-negotiateScheme::done()
+AuthNegotiateConfig::rotateHelpers()
 {
- /* TODO: this should be a Config call. */
- debugs(29, 2, "negotiateScheme::done: shutting down Negotiate authentication.");
-
- if (negotiateauthenticators)
+ /* schedule closure of existing helpers */
+ if (negotiateauthenticators) {
         helperStatefulShutdown(negotiateauthenticators);
-
+ }
+
+ /* NP: dynamic helper restart will ensure they start up again as needed. */
+}
+
+void
+AuthNegotiateConfig::done()
+{
     authnegotiate_initialised = 0;
 
+ if (negotiateauthenticators) {
+ helperStatefulShutdown(negotiateauthenticators);
+ }
+
     if (!shutting_down)
         return;
 
     delete negotiateauthenticators;
     negotiateauthenticators = NULL;
 
+ if (authenticate)
+ wordlistDestroy(&authenticate);
+
     debugs(29, 2, "negotiateScheme::done: Negotiate authentication Shutdown.");
 }
 
 void
-AuthNegotiateConfig::done()
-{
- if (authenticate)
- wordlistDestroy(&authenticate);
-}
-
-void
 AuthNegotiateConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
 {
     wordlist *list = authenticate;
@@ -142,7 +127,7 @@
 
 }
 
-AuthNegotiateConfig::AuthNegotiateConfig() : authenticateChildren(20), keep_alive(1)
+AuthNegotiateConfig::AuthNegotiateConfig() : keep_alive(1)
 { }
 
 void
@@ -178,7 +163,7 @@
 const char *
 AuthNegotiateConfig::type() const
 {
- return negotiateScheme::GetInstance().type();
+ return negotiateScheme::GetInstance()->type();
 }
 
 /**
@@ -240,66 +225,9 @@
 }
 
 /* Negotiate Scheme */
-/* See AuthUserRequest.cc::authenticateDirection for return values */
-int
-AuthNegotiateUserRequest::module_direction()
-{
- /* null auth_user is checked for by authenticateDirection */
-
- if (waiting || client_blob)
- return -1; /* need helper response to continue */
-
- switch (auth_state) {
-
- /* no progress at all. */
-
- case AUTHENTICATE_STATE_NONE:
- debugs(29, 1, "AuthNegotiateUserRequest::direction: called before Negotiate Authenticate for request " << this << "!. Report a bug to squid-dev.");
- return -2; /* error */
-
- case AUTHENTICATE_STATE_FAILED:
- return -2; /* error */
-
-
- case AUTHENTICATE_STATE_IN_PROGRESS:
- assert(server_blob);
- return 1; /* send to client */
-
- case AUTHENTICATE_STATE_DONE:
- return 0; /* do nothing */
-
- case AUTHENTICATE_STATE_INITIAL:
- debugs(29, 1, "AuthNegotiateUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
- return -2;
- }
-
- return -2;
-}
-
-/* add the [proxy]authorisation header */
-void
-AuthNegotiateUserRequest::addHeader(HttpReply * rep, int accel)
-{
- http_hdr_type type;
-
- if (!server_blob)
- return;
-
- /* don't add to authentication error pages */
-
- if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
- || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
- return;
-
- type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
-
- httpHeaderPutStrf(&rep->header, type, "Negotiate %s", server_blob);
-
- safe_free(server_blob);
-}
-
-void
-AuthNegotiateConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request)
+
+void
+AuthNegotiateConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type reqType, HttpRequest * request)
 {
     AuthNegotiateUserRequest *negotiate_request;
 
@@ -321,24 +249,22 @@
             request->flags.proxy_keepalive = 0;
         }
     } else {
- negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request);
-
+ negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request.getRaw());
         assert(negotiate_request != NULL);
 
- switch (negotiate_request->auth_state) {
+ switch (negotiate_request->user()->credentials()) {
 
- case AUTHENTICATE_STATE_FAILED:
+ case AuthUser::Failed:
             /* here it makes sense to drop the connection, as auth is
              * tied to it, even if MAYBE the client could handle it - Kinkie */
             rep->header.delByName("keep-alive");
             request->flags.proxy_keepalive = 0;
             /* fall through */
 
- case AUTHENTICATE_STATE_DONE:
+ case AuthUser::Ok:
             /* Special case: authentication finished OK but disallowed by ACL.
              * Need to start over to give the client another chance.
              */
-
             if (negotiate_request->server_blob) {
                 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
                 httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
@@ -347,26 +273,24 @@
                 debugs(29, 9, "authenticateNegotiateFixErrorHeader: Connection authenticated");
                 httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
             }
-
             break;
 
- case AUTHENTICATE_STATE_NONE:
+ case AuthUser::Unchecked:
             /* semantic change: do not drop the connection.
              * 2.5 implementation used to keep it open - Kinkie */
             debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate'");
             httpHeaderPutStrf(&rep->header, reqType, "Negotiate");
             break;
 
- case AUTHENTICATE_STATE_IN_PROGRESS:
+ case AuthUser::Handshake:
             /* we're waiting for a response from the client. Pass it the blob */
             debugs(29, 9, "AuthNegotiateConfig::fixHeader: Sending type:" << reqType << " header: 'Negotiate " << negotiate_request->server_blob << "'");
             httpHeaderPutStrf(&rep->header, reqType, "Negotiate %s", negotiate_request->server_blob);
             safe_free(negotiate_request->server_blob);
             break;
 
-
         default:
- debugs(29, 0, "AuthNegotiateConfig::fixHeader: state " << negotiate_request->auth_state << ".");
+ debugs(29, DBG_CRITICAL, "AuthNegotiateConfig::fixHeader: state " << negotiate_request->user()->credentials() << ".");
             fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n");
         }
     }
@@ -377,167 +301,10 @@
     debugs(29, 5, "NegotiateUser::~NegotiateUser: doing nothing to clearNegotiate scheme data for '" << this << "'");
 }
 
-static void
-authenticateNegotiateHandleReply(void *data, void *lastserver, char *reply)
+int32_t
+NegotiateUser::ttl() const
 {
- authenticateStateData *r = static_cast<authenticateStateData *>(data);
-
- int valid;
- char *blob, *arg = NULL;
-
- AuthUserRequest *auth_user_request;
- AuthUser *auth_user;
- NegotiateUser *negotiate_user;
- AuthNegotiateUserRequest *negotiate_request;
-
- debugs(29, 8, "authenticateNegotiateHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
- valid = cbdataReferenceValid(r->data);
-
- if (!valid) {
- debugs(29, 1, "authenticateNegotiateHandleReply: invalid callback data. helper '" << lastserver << "'.");
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
- return;
- }
-
- if (!reply) {
- debugs(29, 1, "authenticateNegotiateHandleReply: Helper '" << lastserver << "' crashed!.");
- reply = (char *)"BH Internal error";
- }
-
- auth_user_request = r->auth_user_request;
- assert(auth_user_request != NULL);
- negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request);
-
- assert(negotiate_request != NULL);
- assert(negotiate_request->waiting);
- negotiate_request->waiting = 0;
- safe_free(negotiate_request->client_blob);
-
- auth_user = negotiate_request->user();
- assert(auth_user != NULL);
- assert(auth_user->auth_type == AUTH_NEGOTIATE);
- negotiate_user = dynamic_cast<negotiate_user_t *>(auth_user_request->user());
-
- assert(negotiate_user != NULL);
-
- if (negotiate_request->authserver == NULL)
- negotiate_request->authserver = static_cast<helper_stateful_server*>(lastserver);
- else
- assert(negotiate_request->authserver == lastserver);
-
- /* seperate out the useful data */
- blob = strchr(reply, ' ');
-
- if (blob) {
- blob++;
- arg = strchr(blob + 1, ' ');
- } else {
- arg = NULL;
- }
-
- if (strncasecmp(reply, "TT ", 3) == 0) {
- /* we have been given a blob to send to the client */
- if (arg)
- *arg++ = '\0';
- safe_free(negotiate_request->server_blob);
- negotiate_request->request->flags.must_keepalive = 1;
- if (negotiate_request->request->flags.proxy_keepalive) {
- negotiate_request->server_blob = xstrdup(blob);
- negotiate_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
- auth_user_request->denyMessage("Authentication in progress");
- debugs(29, 4, "authenticateNegotiateHandleReply: Need to challenge the client with a server blob '" << blob << "'");
- } else {
- negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
- auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
- }
- } else if (strncasecmp(reply, "AF ", 3) == 0 && arg != NULL) {
- /* we're finished, release the helper */
-
- if (arg)
- *arg++ = '\0';
-
- negotiate_user->username(arg);
-
- auth_user_request->denyMessage("Login successful");
-
- safe_free(negotiate_request->server_blob);
-
- negotiate_request->server_blob = xstrdup(blob);
-
- negotiate_request->releaseAuthServer();
-
- negotiate_request->auth_state = AUTHENTICATE_STATE_DONE;
-
- debugs(29, 4, "authenticateNegotiateHandleReply: Successfully validated user via Negotiate. Username '" << blob << "'");
-
- /* connection is authenticated */
- debugs(29, 4, "AuthNegotiateUserRequest::authenticate: authenticated user " << negotiate_user->username());
- /* see if this is an existing user with a different proxy_auth
- * string */
- AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, negotiate_user->username()));
- AuthUser *local_auth_user = negotiate_request->user();
- while (usernamehash && (usernamehash->user()->auth_type != AUTH_NEGOTIATE || strcmp(usernamehash->user()->username(), negotiate_user->username()) != 0))
- usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
- if (usernamehash) {
- /* we can't seamlessly recheck the username due to the
- * challenge-response nature of the protocol.
- * Just free the temporary auth_user */
- usernamehash->user()->absorb(local_auth_user);
- //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
- local_auth_user = usernamehash->user();
- negotiate_request->_auth_user = local_auth_user;
- } else {
- /* store user in hash's */
- local_auth_user->addToNameCache();
- // authenticateUserNameCacheAdd(local_auth_user);
- }
- /* set these to now because this is either a new login from an
- * existing user or a new user */
- local_auth_user->expiretime = current_time.tv_sec;
- negotiate_request->releaseAuthServer();
- negotiate_request->auth_state = AUTHENTICATE_STATE_DONE;
-
- } else if (strncasecmp(reply, "NA ", 3) == 0 && arg != NULL) {
- /* authentication failure (wrong password, etc.) */
-
- if (arg)
- *arg++ = '\0';
-
- auth_user_request->denyMessage(arg);
-
- negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
-
- safe_free(negotiate_request->server_blob);
-
- negotiate_request->server_blob = xstrdup(blob);
-
- negotiate_request->releaseAuthServer();
-
- debugs(29, 4, "authenticateNegotiateHandleReply: Failed validating user via Negotiate. Error returned '" << blob << "'");
- } else if (strncasecmp(reply, "BH ", 3) == 0) {
- /* TODO kick off a refresh process. This can occur after a YR or after
- * a KK. If after a YR release the helper and resubmit the request via
- * Authenticate Negotiate start.
- * If after a KK deny the user's request w/ 407 and mark the helper as
- * Needing YR. */
- auth_user_request->denyMessage(blob);
- negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
- safe_free(negotiate_request->server_blob);
- negotiate_request->releaseAuthServer();
- debugs(29, 1, "authenticateNegotiateHandleReply: Error validating user via Negotiate. Error returned '" << reply << "'");
- } else {
- /* protocol error */
- fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
- }
-
- if (negotiate_request->request) {
- HTTPMSGUNLOCK(negotiate_request->request);
- negotiate_request->request = NULL;
- }
- r->handler(r->data, NULL);
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
+ return -1; // Negotiate cannot be cached.
 }
 
 static void
@@ -546,273 +313,32 @@
     helperStatefulStats(sentry, negotiateauthenticators, "Negotiate Authenticator Statistics");
 }
 
-
-/** send the initial data to a stateful negotiate authenticator module */
-void
-AuthNegotiateUserRequest::module_start(RH * handler, void *data)
-{
- authenticateStateData *r = NULL;
- static char buf[MAX_AUTHTOKEN_LEN];
- negotiate_user_t *negotiate_user;
- AuthUser *auth_user = user();
-
- assert(data);
- assert(handler);
- assert(auth_user);
- assert(auth_user->auth_type == AUTH_NEGOTIATE);
-
- negotiate_user = dynamic_cast<negotiate_user_t *>(user());
-
- debugs(29, 8, "AuthNegotiateUserRequest::module_start: auth state is '" << auth_state << "'");
-
- if (negotiateConfig.authenticate == NULL) {
- debugs(29, 0, "AuthNegotiateUserRequest::module_start: no Negotiate program specified.");
- handler(data, NULL);
- return;
- }
-
- r = cbdataAlloc(authenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = this;
- AUTHUSERREQUESTLOCK(r->auth_user_request, "r");
-
- if (auth_state == AUTHENTICATE_STATE_INITIAL) {
- snprintf(buf, MAX_AUTHTOKEN_LEN, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
- } else {
- snprintf(buf, MAX_AUTHTOKEN_LEN, "KK %s\n", client_blob);
- }
-
- waiting = 1;
-
- safe_free(client_blob);
- helperStatefulSubmit(negotiateauthenticators, buf, authenticateNegotiateHandleReply, r, authserver);
-}
-
-/**
- * Atomic action: properly release the Negotiate auth helpers which may have been reserved
- * for this request connections use.
- */
-void
-AuthNegotiateUserRequest::releaseAuthServer()
-{
- if (authserver) {
- debugs(29, 6, HERE << "releasing Negotiate auth server '" << authserver << "'");
- helperStatefulReleaseServer(authserver);
- authserver = NULL;
- } else
- debugs(29, 6, HERE << "No Negotiate auth server to release.");
-}
-
-/* clear any connection related authentication details */
-void
-AuthNegotiateUserRequest::onConnectionClose(ConnStateData *conn)
-{
- assert(conn != NULL);
-
- debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
-
- if (conn->auth_user_request == NULL) {
- debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: no auth_user_request");
- return;
- }
-
- releaseAuthServer();
-
- /* unlock the connection based lock */
- debugs(29, 9, "AuthNegotiateUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
-
- AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
-}
-
 /*
  * Decode a Negotiate [Proxy-]Auth string, placing the results in the passed
  * Auth_user structure.
  */
-AuthUserRequest *
+AuthUserRequest::Pointer
 AuthNegotiateConfig::decode(char const *proxy_auth)
 {
     NegotiateUser *newUser = new NegotiateUser(&negotiateConfig);
- AuthNegotiateUserRequest *auth_user_request = new AuthNegotiateUserRequest ();
+ AuthUserRequest *auth_user_request = new AuthNegotiateUserRequest();
     assert(auth_user_request->user() == NULL);
+
     auth_user_request->user(newUser);
     auth_user_request->user()->auth_type = AUTH_NEGOTIATE;
- auth_user_request->user()->addRequest(auth_user_request);
 
     /* all we have to do is identify that it's Negotiate - the helper does the rest */
     debugs(29, 9, "AuthNegotiateConfig::decode: Negotiate authentication");
     return auth_user_request;
 }
 
-int
-AuthNegotiateUserRequest::authenticated() const
-{
- if (auth_state == AUTHENTICATE_STATE_DONE) {
- debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user authenticated.");
- return 1;
- }
-
- debugs(29, 9, "AuthNegotiateUserRequest::authenticated: user not fully authenticated.");
-
- return 0;
-}
-
-void
-AuthNegotiateUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
-{
- const char *proxy_auth, *blob;
-
- /** \todo rename this!! */
- AuthUser *local_auth_user;
- negotiate_user_t *negotiate_user;
-
- local_auth_user = user();
- assert(local_auth_user);
- assert(local_auth_user->auth_type == AUTH_NEGOTIATE);
- negotiate_user = dynamic_cast<negotiate_user_t *>(local_auth_user);
- assert (this);
-
- /** Check that we are in the client side, where we can generate
- * auth challenges */
-
- if (conn == NULL) {
- auth_state = AUTHENTICATE_STATE_FAILED;
- debugs(29, 1, "AuthNegotiateUserRequest::authenticate: attempt to perform authentication without a connection!");
- return;
- }
-
- if (waiting) {
- debugs(29, 1, "AuthNegotiateUserRequest::authenticate: waiting for helper reply!");
- return;
- }
-
- if (server_blob) {
- debugs(29, 2, "AuthNegotiateUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
- return;
- }
-
- /* get header */
- proxy_auth = aRequest->header.getStr(type);
-
- /* locate second word */
- blob = proxy_auth;
-
- if (blob) {
- while (xisspace(*blob) && *blob)
- blob++;
-
- while (!xisspace(*blob) && *blob)
- blob++;
-
- while (xisspace(*blob) && *blob)
- blob++;
- }
-
- switch (auth_state) {
-
- case AUTHENTICATE_STATE_NONE:
- /* we've received a negotiate request. pass to a helper */
- debugs(29, 9, "AuthNegotiateUserRequest::authenticate: auth state negotiate none. Received blob: '" << proxy_auth << "'");
- auth_state = AUTHENTICATE_STATE_INITIAL;
- safe_free(client_blob);
- client_blob=xstrdup(blob);
- conn->auth_type = AUTH_NEGOTIATE;
- assert(conn->auth_user_request == NULL);
- conn->auth_user_request = this;
- AUTHUSERREQUESTLOCK(conn->auth_user_request, "conn");
- request = aRequest;
- HTTPMSGLOCK(request);
- return;
-
- break;
-
- case AUTHENTICATE_STATE_INITIAL:
- debugs(29, 1, "AuthNegotiateUserRequest::authenticate: need to ask helper");
-
- return;
-
- break;
-
-
- case AUTHENTICATE_STATE_IN_PROGRESS:
- /* we should have received a blob from the client. Hand it off to
- * some helper */
- safe_free(client_blob);
-
- client_blob = xstrdup (blob);
-
- if (request)
- HTTPMSGUNLOCK(request);
- request = aRequest;
- HTTPMSGLOCK(request);
- return;
-
- break;
-
- case AUTHENTICATE_STATE_DONE:
- fatal("AuthNegotiateUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
-
- break;
-
- case AUTHENTICATE_STATE_FAILED:
- /* we've failed somewhere in authentication */
- debugs(29, 9, "AuthNegotiateUserRequest::authenticate: auth state negotiate failed. " << proxy_auth);
-
- return;
-
- break;
- }
-
- return;
-}
-
-AuthNegotiateUserRequest::AuthNegotiateUserRequest() :
- /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE),
- _theUser(NULL)
-{
- waiting=0;
- client_blob=0;
- server_blob=0;
- authserver=NULL;
- request=NULL;
-}
-
-AuthNegotiateUserRequest::~AuthNegotiateUserRequest()
-{
- safe_free(server_blob);
- safe_free(client_blob);
-
- if (authserver != NULL) {
- debugs(29, 9, "AuthNegotiateUserRequest::~AuthNegotiateUserRequest: releasing server '" << authserver << "'");
- helperStatefulReleaseServer(authserver);
- authserver = NULL;
- }
- if (request) {
- HTTPMSGUNLOCK(request);
- request = NULL;
- }
-}
-
 void
 NegotiateUser::deleteSelf() const
 {
     delete this;
 }
 
-NegotiateUser::NegotiateUser (AuthConfig *aConfig) : AuthUser (aConfig)
+NegotiateUser::NegotiateUser(AuthConfig *aConfig) : AuthUser (aConfig)
 {
     proxy_auth_list.head = proxy_auth_list.tail = NULL;
 }
-
-AuthConfig *
-negotiateScheme::createConfig()
-{
- return &negotiateConfig;
-}
-
-const char *
-AuthNegotiateUserRequest::connLastHeader()
-{
- return NULL;
-}
-

=== modified file 'src/auth/negotiate/auth_negotiate.h'
--- src/auth/negotiate/auth_negotiate.h 2009-12-16 03:46:59 +0000
+++ src/auth/negotiate/auth_negotiate.h 2010-04-26 07:07:44 +0000
@@ -5,10 +5,12 @@
 
 #ifndef __AUTH_NEGOTIATE_H__
 #define __AUTH_NEGOTIATE_H__
+
+#include "auth/Config.h"
 #include "auth/Gadgets.h"
+#include "auth/State.h"
 #include "auth/User.h"
 #include "auth/UserRequest.h"
-#include "auth/Config.h"
 #include "helper.h"
 
 /**
@@ -19,98 +21,23 @@
 /// \ingroup AuthNegotiateAPI
 #define DefaultAuthenticateChildrenMax 32 /* 32 processes */
 
-#ifndef __AUTH_AUTHENTICATE_STATE_T__
-#define __AUTH_AUTHENTICATE_STATE_T__
-
-/// \ingroup AuthNegotiateAPI
-typedef enum {
- AUTHENTICATE_STATE_NONE,
- AUTHENTICATE_STATE_INITIAL,
- AUTHENTICATE_STATE_IN_PROGRESS,
- AUTHENTICATE_STATE_DONE,
- AUTHENTICATE_STATE_FAILED
-} auth_state_t; /* connection level auth state */
-
-/* Generic */
-
-/// \ingroup AuthNegotiateAPI
-typedef struct {
- void *data;
- AuthUserRequest *auth_user_request;
- RH *handler;
-} authenticateStateData;
-#endif
-
 /// \ingroup AuthNegotiateAPI
 class NegotiateUser : public AuthUser
 {
 
 public:
     MEMPROXY_CLASS(NegotiateUser);
- virtual void deleteSelf() const;
     NegotiateUser(AuthConfig *);
     ~NegotiateUser();
+ virtual void deleteSelf() const;
+ virtual int32_t ttl() const;
+
     dlink_list proxy_auth_list;
 };
 
 MEMPROXY_CLASS_INLINE(NegotiateUser);
 
-/// \ingroup AuthNegotiateAPI
-typedef class NegotiateUser negotiate_user_t;
-
-/// \ingroup AuthNegotiateAPI
-class AuthNegotiateUserRequest : public AuthUserRequest
-{
-
-public:
- MEMPROXY_CLASS(AuthNegotiateUserRequest);
-
- AuthNegotiateUserRequest();
- virtual ~AuthNegotiateUserRequest();
- virtual int authenticated() const;
- virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
- virtual int module_direction();
- virtual void onConnectionClose(ConnStateData *);
- virtual void module_start(RH *, void *);
- virtual AuthUser *user() {return _theUser;}
-
- virtual const AuthUser *user() const {return _theUser;}
-
- virtual void addHeader(HttpReply * rep, int accel);
-
- virtual void user (AuthUser *aUser) {_theUser=dynamic_cast<NegotiateUser *>(aUser);}
-
- virtual const char * connLastHeader();
-
- /*we need to store the helper server between requests */
- helper_stateful_server *authserver;
- void releaseAuthServer(void); ///< Release the authserver helper server properly.
-
- /* what connection is this associated with */
- /* ConnStateData * conn;*/
-
- /* how far through the authentication process are we? */
- auth_state_t auth_state;
-
- /* our current blob to pass to the client */
- char *server_blob;
- /* our current blob to pass to the server */
- char *client_blob;
-
- /* currently waiting for helper response */
- unsigned char waiting;
-
- /* need access to the request flags to mess around on pconn failure */
- HttpRequest *request;
-
-private:
- /* the user */
- NegotiateUser * _theUser;
-};
-
-MEMPROXY_CLASS_INLINE(AuthNegotiateUserRequest);
-
-#include "HelperChildConfig.h"
+extern statefulhelper *negotiateauthenticators;
 
 /* configuration runtime data */
 
@@ -122,20 +49,18 @@
     AuthNegotiateConfig();
     virtual bool active() const;
     virtual bool configured() const;
- virtual AuthUserRequest *decode(char const *proxy_auth);
+ virtual AuthUserRequest::Pointer decode(char const *proxy_auth);
     virtual void done();
+ virtual void rotateHelpers();
     virtual void dump(StoreEntry *, const char *, AuthConfig *);
- virtual void fixHeader(AuthUserRequest *, HttpReply *, http_hdr_type, HttpRequest *);
+ virtual void fixHeader(AuthUserRequest::Pointer, HttpReply *, http_hdr_type, HttpRequest *);
     virtual void init(AuthConfig *);
     virtual void parse(AuthConfig *, int, char *);
     virtual void registerWithCacheManager(void);
     virtual const char * type() const;
- HelperChildConfig authenticateChildren;
     int keep_alive;
- wordlist *authenticate;
 };
 
-/// \ingroup AuthNegotiateAPI
-typedef class AuthNegotiateConfig auth_negotiate_config;
+extern AuthNegotiateConfig negotiateConfig;
 
 #endif

=== modified file 'src/auth/negotiate/negotiateScheme.cc'
--- src/auth/negotiate/negotiateScheme.cc 2009-05-20 06:59:04 +0000
+++ src/auth/negotiate/negotiateScheme.cc 2010-01-17 11:58:04 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -31,19 +30,18 @@
  *
  */
 
-#include "negotiateScheme.h"
+#include "config.h"
+#include "auth/negotiate/negotiateScheme.h"
+#include "helper.h"
 
-AuthScheme &
+AuthScheme::Pointer
 negotiateScheme::GetInstance()
 {
- if (_instance == NULL)
+ if (_instance == NULL) {
         _instance = new negotiateScheme();
- return *_instance;
-}
-
-negotiateScheme::negotiateScheme()
-{
- AddScheme(*this);
+ AddScheme(_instance);
+ }
+ return _instance;
 }
 
 char const *
@@ -52,4 +50,24 @@
     return "negotiate";
 }
 
-negotiateScheme *negotiateScheme::_instance = NULL;
+AuthScheme::Pointer negotiateScheme::_instance = NULL;
+
+/**
+ \ingroup AuthNegotiateInternal
+ \todo move to negotiateScheme.cc
+ */
+void
+negotiateScheme::done()
+{
+ /* clear the global handle to this scheme. */
+ _instance = NULL;
+
+ debugs(29, 2, "negotiateScheme::done: Negotiate authentication Shutdown.");
+}
+
+AuthConfig *
+negotiateScheme::createConfig()
+{
+ AuthNegotiateConfig *negotiateCfg = new AuthNegotiateConfig;
+ return dynamic_cast<AuthConfig*>(negotiateCfg);
+}

=== modified file 'src/auth/negotiate/negotiateScheme.h'
--- src/auth/negotiate/negotiateScheme.h 2009-05-20 06:59:04 +0000
+++ src/auth/negotiate/negotiateScheme.h 2010-01-17 11:58:04 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -35,6 +34,7 @@
 #define SQUID_NEGOTIATESCHEME_H
 
 #include "auth/Scheme.h"
+#include "auth/negotiate/auth_negotiate.h"
 
 /// \ingroup AuthSchemeAPI
 /// \ingroup AuthAPI
@@ -42,20 +42,21 @@
 {
 
 public:
- static AuthScheme &GetInstance();
- negotiateScheme();
+ static AuthScheme::Pointer GetInstance();
+ negotiateScheme() {};
     virtual ~negotiateScheme() {};
 
     /* per scheme */
     virtual char const *type () const;
     virtual void done();
     virtual AuthConfig *createConfig();
+
     /* Not implemented */
     negotiateScheme (negotiateScheme const &);
     negotiateScheme &operator=(negotiateScheme const &);
 
 private:
- static negotiateScheme *_instance;
+ static AuthScheme::Pointer _instance;
 };
 
 #endif /* SQUID_negotiateSCHEME_H */

=== added file 'src/auth/negotiate/negotiateUserRequest.cc'
--- src/auth/negotiate/negotiateUserRequest.cc 1970-01-01 00:00:00 +0000
+++ src/auth/negotiate/negotiateUserRequest.cc 2010-05-06 11:07:19 +0000
@@ -0,0 +1,404 @@
+#include "config.h"
+#include "auth/negotiate/auth_negotiate.h"
+#include "auth/negotiate/negotiateUserRequest.h"
+#include "auth/User.h"
+#include "helper.h"
+#include "HttpReply.h"
+#include "HttpRequest.h"
+#include "SquidTime.h"
+
+/**
+ * Maximum length (buffer size) for token strings.
+ */
+// AYJ: must match re-definition in helpers/negotiate_auth/kerberos/negotiate_kerb_auth.cc
+#define MAX_AUTHTOKEN_LEN 32768
+
+AuthNegotiateUserRequest::AuthNegotiateUserRequest()
+{
+ waiting=0;
+ client_blob=0;
+ server_blob=0;
+ authserver=NULL;
+ request=NULL;
+}
+
+AuthNegotiateUserRequest::~AuthNegotiateUserRequest()
+{
+ assert(RefCountCount()==0);
+ safe_free(server_blob);
+ safe_free(client_blob);
+
+ if (authserver != NULL) {
+ debugs(29, 9, HERE << "releasing server '" << authserver << "'");
+ helperStatefulReleaseServer(authserver);
+ authserver = NULL;
+ }
+ if (request) {
+ HTTPMSGUNLOCK(request);
+ request = NULL;
+ }
+}
+
+const char *
+AuthNegotiateUserRequest::connLastHeader()
+{
+ return NULL;
+}
+
+int
+AuthNegotiateUserRequest::authenticated() const
+{
+ if (user() != NULL && user()->credentials() == AuthUser::Ok) {
+ debugs(29, 9, HERE << "user authenticated.");
+ return 1;
+ }
+
+ debugs(29, 9, HERE << "user not fully authenticated.");
+ return 0;
+}
+
+/* See AuthUserRequest.cc::authenticateDirection for return values */
+int
+AuthNegotiateUserRequest::module_direction()
+{
+ /* null auth_user is checked for by authenticateDirection */
+
+ if (waiting || client_blob)
+ return -1; /* need helper response to continue */
+
+ if (user()->auth_type != AUTH_NEGOTIATE)
+ return -2;
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Handshake:
+ assert(server_blob);
+ return 1; /* send to client */
+
+ case AuthUser::Ok:
+ return 0; /* do nothing */
+
+ case AuthUser::Failed:
+ return -2;
+
+ default:
+ debugs(29, DBG_IMPORTANT, "WARNING: Negotiate Authentication in unexpected state: " << user()->credentials());
+ return -2;
+ }
+}
+
+/* add the [proxy]authorisation header */
+void
+AuthNegotiateUserRequest::addHeader(HttpReply * rep, int accel)
+{
+ http_hdr_type type;
+
+ if (!server_blob)
+ return;
+
+ /* don't add to authentication error pages */
+ if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
+ || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
+ return;
+
+ type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
+ httpHeaderPutStrf(&rep->header, type, "Negotiate %s", server_blob);
+
+ safe_free(server_blob);
+}
+
+/** send the initial data to a stateful negotiate authenticator module */
+void
+AuthNegotiateUserRequest::module_start(RH * handler, void *data)
+{
+ static char buf[MAX_AUTHTOKEN_LEN];
+
+ assert(data);
+ assert(handler);
+
+ assert(user() != NULL);
+ assert(user()->auth_type == AUTH_NEGOTIATE);
+
+ debugs(29, 8, HERE << "auth state is '" << user()->credentials() << "'");
+
+ if (static_cast<AuthNegotiateConfig*>(AuthConfig::Find("negotiate"))->authenticate == NULL) {
+ debugs(29, DBG_CRITICAL, "ERROR: No Negotiate authentication program configured.");
+ handler(data, NULL);
+ return;
+ }
+
+ authenticateStateData *r = cbdataAlloc(authenticateStateData);
+ r->handler = handler;
+ r->data = cbdataReference(data);
+ r->auth_user_request = this;
+
+ if (user()->credentials() == AuthUser::Pending) {
+ snprintf(buf, MAX_AUTHTOKEN_LEN, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
+ } else {
+ snprintf(buf, MAX_AUTHTOKEN_LEN, "KK %s\n", client_blob);
+ }
+
+ waiting = 1;
+
+ safe_free(client_blob);
+ helperStatefulSubmit(negotiateauthenticators, buf, AuthNegotiateUserRequest::HandleReply, r, authserver);
+}
+
+/**
+ * Atomic action: properly release the Negotiate auth helpers which may have been reserved
+ * for this request connections use.
+ */
+void
+AuthNegotiateUserRequest::releaseAuthServer()
+{
+ if (authserver) {
+ debugs(29, 6, HERE << "releasing Negotiate auth server '" << authserver << "'");
+ helperStatefulReleaseServer(authserver);
+ authserver = NULL;
+ } else
+ debugs(29, 6, HERE << "No Negotiate auth server to release.");
+}
+
+/* clear any connection related authentication details */
+void
+AuthNegotiateUserRequest::onConnectionClose(ConnStateData *conn)
+{
+ assert(conn != NULL);
+
+ debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
+
+ if (conn->auth_user_request == NULL) {
+ debugs(29, 8, "AuthNegotiateUserRequest::onConnectionClose: no auth_user_request");
+ return;
+ }
+
+ releaseAuthServer();
+
+ /* unlock the connection based lock */
+ debugs(29, 9, "AuthNegotiateUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
+
+ conn->auth_user_request = NULL;
+}
+
+void
+AuthNegotiateUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
+{
+ assert (this);
+
+ /** Check that we are in the client side, where we can generate auth challenges */
+ if (conn == NULL) {
+ user()->credentials(AuthUser::Failed);
+ debugs(29, DBG_IMPORTANT, "WARNING: Negotiate Authentication attempt to perform authentication without a connection!");
+ return;
+ }
+
+ if (waiting) {
+ debugs(29, DBG_IMPORTANT, "WARNING: Negotiate Authentication waiting for helper reply!");
+ return;
+ }
+
+ if (server_blob) {
+ debugs(29, 2, HERE << "need to challenge client '" << server_blob << "'!");
+ return;
+ }
+
+ /* get header */
+ const char *proxy_auth = aRequest->header.getStr(type);
+
+ /* locate second word */
+ const char *blob = proxy_auth;
+
+ if (blob) {
+ while (xisspace(*blob) && *blob)
+ blob++;
+
+ while (!xisspace(*blob) && *blob)
+ blob++;
+
+ while (xisspace(*blob) && *blob)
+ blob++;
+ }
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Unchecked:
+ /* we've received a negotiate request. pass to a helper */
+ debugs(29, 9, HERE << "auth state negotiate none. Received blob: '" << proxy_auth << "'");
+ user()->credentials(AuthUser::Pending);
+ safe_free(client_blob);
+ client_blob=xstrdup(blob);
+ assert(conn->auth_user_request == NULL);
+ conn->auth_user_request = this;
+ request = aRequest;
+ HTTPMSGLOCK(request);
+ break;
+
+ case AuthUser::Pending:
+ debugs(29, 1, HERE << "need to ask helper");
+ break;
+
+ case AuthUser::Handshake:
+ /* we should have received a blob from the client. Hand it off to
+ * some helper */
+ safe_free(client_blob);
+ client_blob = xstrdup(blob);
+ if (request)
+ HTTPMSGUNLOCK(request);
+ request = aRequest;
+ HTTPMSGLOCK(request);
+ break;
+
+ case AuthUser::Ok:
+ fatal("AuthNegotiateUserRequest::authenticate: unexpected auth state DONE! Report a bug to the squid developers.\n");
+ break;
+
+ case AuthUser::Failed:
+ /* we've failed somewhere in authentication */
+ debugs(29, 9, HERE << "auth state negotiate failed. " << proxy_auth);
+ break;
+ }
+
+ return;
+}
+
+void
+AuthNegotiateUserRequest::HandleReply(void *data, void *lastserver, char *reply)
+{
+ authenticateStateData *r = static_cast<authenticateStateData *>(data);
+
+ int valid;
+ char *blob, *arg = NULL;
+
+ debugs(29, 8, HERE << "helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
+ valid = cbdataReferenceValid(r->data);
+
+ if (!valid) {
+ debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication invalid callback data. helper '" << lastserver << "'.");
+ cbdataReferenceDone(r->data);
+ authenticateStateFree(r);
+ return;
+ }
+
+ if (!reply) {
+ debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication Helper '" << lastserver << "' crashed!.");
+ reply = (char *)"BH Internal error";
+ }
+
+ AuthUserRequest::Pointer auth_user_request = r->auth_user_request;
+ assert(auth_user_request != NULL);
+
+ AuthNegotiateUserRequest *negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request.getRaw());
+ assert(negotiate_request != NULL);
+
+ assert(negotiate_request->waiting);
+ negotiate_request->waiting = 0;
+ safe_free(negotiate_request->client_blob);
+
+ assert(auth_user_request->user() != NULL);
+ assert(auth_user_request->user()->auth_type == AUTH_NEGOTIATE);
+
+ if (negotiate_request->authserver == NULL)
+ negotiate_request->authserver = static_cast<helper_stateful_server*>(lastserver);
+ else
+ assert(negotiate_request->authserver == lastserver);
+
+ /* seperate out the useful data */
+ blob = strchr(reply, ' ');
+
+ if (blob) {
+ blob++;
+ arg = strchr(blob + 1, ' ');
+ } else {
+ arg = NULL;
+ }
+
+ if (strncasecmp(reply, "TT ", 3) == 0) {
+ /* we have been given a blob to send to the client */
+ if (arg)
+ *arg++ = '\0';
+ safe_free(negotiate_request->server_blob);
+ negotiate_request->request->flags.must_keepalive = 1;
+ if (negotiate_request->request->flags.proxy_keepalive) {
+ negotiate_request->server_blob = xstrdup(blob);
+ auth_user_request->user()->credentials(AuthUser::Handshake);
+ auth_user_request->denyMessage("Authentication in progress");
+ debugs(29, 4, HERE << "Need to challenge the client with a server blob '" << blob << "'");
+ } else {
+ auth_user_request->user()->credentials(AuthUser::Failed);
+ auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
+ }
+ } else if (strncasecmp(reply, "AF ", 3) == 0 && arg != NULL) {
+ /* we're finished, release the helper */
+
+ if (arg)
+ *arg++ = '\0';
+
+ auth_user_request->user()->username(arg);
+ auth_user_request->denyMessage("Login successful");
+ safe_free(negotiate_request->server_blob);
+ negotiate_request->server_blob = xstrdup(blob);
+ negotiate_request->releaseAuthServer();
+ auth_user_request->user()->credentials(AuthUser::Ok);
+ debugs(29, 4, HERE << "Successfully validated user via Negotiate. Username '" << blob << "'");
+
+ /* connection is authenticated */
+ debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username());
+ /* see if this is an existing user with a different proxy_auth
+ * string */
+ AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username()));
+ AuthUser::Pointer local_auth_user = negotiate_request->user();
+ while (usernamehash && (usernamehash->user()->auth_type != AUTH_NEGOTIATE || strcmp(usernamehash->user()->username(), auth_user_request->user()->username()) != 0))
+ usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
+ if (usernamehash) {
+ /* we can't seamlessly recheck the username due to the
+ * challenge-response nature of the protocol.
+ * Just free the temporary auth_user after merging as
+ * much of it new state into the existing one as possible */
+ usernamehash->user()->absorb(local_auth_user);
+ local_auth_user = usernamehash->user();
+ /* from here on we are working with the original cached credentials. */
+ negotiate_request->_auth_user = local_auth_user;
+ } else {
+ /* store user in hash's */
+ local_auth_user->addToNameCache();
+ }
+ /* set these to now because this is either a new login from an
+ * existing user or a new user */
+ local_auth_user->expiretime = current_time.tv_sec;
+ negotiate_request->releaseAuthServer();
+ negotiate_request->user()->credentials(AuthUser::Ok);
+
+ } else if (strncasecmp(reply, "NA ", 3) == 0 && arg != NULL) {
+ /* authentication failure (wrong password, etc.) */
+
+ if (arg)
+ *arg++ = '\0';
+
+ auth_user_request->denyMessage(arg);
+ negotiate_request->user()->credentials(AuthUser::Failed);
+ safe_free(negotiate_request->server_blob);
+ negotiate_request->server_blob = xstrdup(blob);
+ negotiate_request->releaseAuthServer();
+ debugs(29, 4, HERE << "Failed validating user via Negotiate. Error returned '" << blob << "'");
+ } else if (strncasecmp(reply, "BH ", 3) == 0) {
+ /* TODO kick off a refresh process. This can occur after a YR or after
+ * a KK. If after a YR release the helper and resubmit the request via
+ * Authenticate Negotiate start.
+ * If after a KK deny the user's request w/ 407 and mark the helper as
+ * Needing YR. */
+ auth_user_request->denyMessage(blob);
+ auth_user_request->user()->credentials(AuthUser::Failed);
+ safe_free(negotiate_request->server_blob);
+ negotiate_request->releaseAuthServer();
+ debugs(29, DBG_IMPORTANT, "ERROR: Negotiate Authentication validating user. Error returned '" << reply << "'");
+ } else {
+ /* protocol error */
+ fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
+ }
+
+ negotiate_request->request = NULL;
+ r->handler(r->data, NULL);
+ cbdataReferenceDone(r->data);
+ authenticateStateFree(r);
+}
+

=== added file 'src/auth/negotiate/negotiateUserRequest.h'
--- src/auth/negotiate/negotiateUserRequest.h 1970-01-01 00:00:00 +0000
+++ src/auth/negotiate/negotiateUserRequest.h 2010-05-06 11:07:19 +0000
@@ -0,0 +1,56 @@
+#ifndef _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H
+#define _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H
+
+#include "auth/UserRequest.h"
+#include "helper.h"
+#include "MemPool.h"
+
+class ConnStateData;
+class HttpReply;
+class HttpRequest;
+struct helper_stateful_server;
+
+/// \ingroup AuthNegotiateAPI
+class AuthNegotiateUserRequest : public AuthUserRequest
+{
+
+public:
+ MEMPROXY_CLASS(AuthNegotiateUserRequest);
+
+ AuthNegotiateUserRequest();
+ virtual ~AuthNegotiateUserRequest();
+ virtual int authenticated() const;
+ virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
+ virtual int module_direction();
+ virtual void onConnectionClose(ConnStateData *);
+ virtual void module_start(RH *, void *);
+
+ virtual void addHeader(HttpReply * rep, int accel);
+
+ virtual const char * connLastHeader();
+
+ /* we need to store the helper server between requests */
+ helper_stateful_server *authserver;
+ void releaseAuthServer(void); ///< Release the authserver helper server properly.
+
+ /* what connection is this associated with */
+ /* ConnStateData * conn;*/
+
+ /* our current blob to pass to the client */
+ char *server_blob;
+ /* our current blob to pass to the server */
+ char *client_blob;
+
+ /* currently waiting for helper response */
+ unsigned char waiting;
+
+ /* need access to the request flags to mess around on pconn failure */
+ HttpRequest *request;
+
+private:
+ static HLPSCB HandleReply;
+};
+
+MEMPROXY_CLASS_INLINE(AuthNegotiateUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_NEGOTIATE_USERREQUEST_H */

=== modified file 'src/auth/ntlm/auth_ntlm.cc'
--- src/auth/ntlm/auth_ntlm.cc 2010-02-13 09:16:30 +0000
+++ src/auth/ntlm/auth_ntlm.cc 2010-05-06 11:07:19 +0000
@@ -38,38 +38,25 @@
 
 
 #include "squid.h"
-#include "auth_ntlm.h"
 #include "auth/Gadgets.h"
+#include "auth/ntlm/auth_ntlm.h"
+#include "auth/ntlm/ntlmScheme.h"
+#include "auth/ntlm/ntlmUserRequest.h"
+#include "auth/State.h"
 #include "CacheManager.h"
 #include "Store.h"
 #include "client_side.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
-/* TODO remove this include */
-#include "ntlmScheme.h"
 #include "wordlist.h"
 #include "SquidTime.h"
 
-
-static void
-authenticateStateFree(authenticateStateData * r)
-{
- AUTHUSERREQUESTUNLOCK(r->auth_user_request, "r");
- cbdataFree(r);
-}
-
 /* NTLM Scheme */
-static HLPSCB authenticateNTLMHandleReply;
 static AUTHSSTATS authenticateNTLMStats;
 
-static statefulhelper *ntlmauthenticators = NULL;
-
-CBDATA_TYPE(authenticateStateData);
-
+statefulhelper *ntlmauthenticators = NULL;
 static int authntlm_initialised = 0;
 
-static auth_ntlm_config ntlmConfig;
-
 static hash_table *proxy_auth_cache = NULL;
 
 /*
@@ -78,35 +65,39 @@
  *
  */
 
-/* move to ntlmScheme.cc */
 void
-ntlmScheme::done()
+AuthNTLMConfig::rotateHelpers()
 {
- /* TODO: this should be a Config call. */
- debugs(29, 2, "ntlmScheme::done: shutting down NTLM authentication.");
-
- if (ntlmauthenticators)
+ /* schedule closure of existing helpers */
+ if (ntlmauthenticators) {
         helperStatefulShutdown(ntlmauthenticators);
-
+ }
+
+ /* NP: dynamic helper restart will ensure they start up again as needed. */
+}
+
+/* free any allocated configuration details */
+void
+AuthNTLMConfig::done()
+{
     authntlm_initialised = 0;
 
+ if (ntlmauthenticators) {
+ helperStatefulShutdown(ntlmauthenticators);
+ }
+
     if (!shutting_down)
         return;
 
     delete ntlmauthenticators;
     ntlmauthenticators = NULL;
 
+ if (authenticate)
+ wordlistDestroy(&authenticate);
+
     debugs(29, 2, "ntlmScheme::done: NTLM authentication Shutdown.");
 }
 
-/* free any allocated configuration details */
-void
-AuthNTLMConfig::done()
-{
- if (authenticate)
- wordlistDestroy(&authenticate);
-}
-
 void
 AuthNTLMConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
 {
@@ -124,7 +115,7 @@
 
 }
 
-AuthNTLMConfig::AuthNTLMConfig() : authenticateChildren(20), keep_alive(1)
+AuthNTLMConfig::AuthNTLMConfig() : keep_alive(1)
 { }
 
 void
@@ -160,7 +151,7 @@
 const char *
 AuthNTLMConfig::type() const
 {
- return ntlmScheme::GetInstance().type();
+ return ntlmScheme::GetInstance()->type();
 }
 
 /* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
@@ -220,47 +211,10 @@
 }
 
 /* NTLM Scheme */
-/* See AuthUserRequest.cc::authenticateDirection for return values */
-int
-AuthNTLMUserRequest::module_direction()
-{
- /* null auth_user is checked for by authenticateDirection */
-
- if (waiting || client_blob)
- return -1; /* need helper response to continue */
-
- switch (auth_state) {
-
- /* no progress at all. */
-
- case AUTHENTICATE_STATE_NONE:
- debugs(29, 1, "AuthNTLMUserRequest::direction: called before NTLM Authenticate for request " << this << "!. Report a bug to squid-dev.");
- return -2; /* error */
-
- case AUTHENTICATE_STATE_FAILED:
- return -2; /* error */
-
-
- case AUTHENTICATE_STATE_IN_PROGRESS:
- assert(server_blob);
- return 1; /* send to client */
-
- case AUTHENTICATE_STATE_DONE:
- return 0; /* do nothing */
-
- case AUTHENTICATE_STATE_INITIAL:
- debugs(29, 1, "AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
- return -2;
- }
-
- return -2;
-}
 
 void
-AuthNTLMConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
+AuthNTLMConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
 {
- AuthNTLMUserRequest *ntlm_request;
-
     if (!authenticate)
         return;
 
@@ -278,41 +232,39 @@
             request->flags.proxy_keepalive = 0;
         }
     } else {
- ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
-
+ AuthNTLMUserRequest *ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request.getRaw());
         assert(ntlm_request != NULL);
 
- switch (ntlm_request->auth_state) {
+ switch (ntlm_request->user()->credentials()) {
 
- case AUTHENTICATE_STATE_FAILED:
+ case AuthUser::Failed:
             /* here it makes sense to drop the connection, as auth is
              * tied to it, even if MAYBE the client could handle it - Kinkie */
             request->flags.proxy_keepalive = 0;
             /* fall through */
 
- case AUTHENTICATE_STATE_DONE:
+ case AuthUser::Ok:
             /* Special case: authentication finished OK but disallowed by ACL.
              * Need to start over to give the client another chance.
              */
             /* fall through */
 
- case AUTHENTICATE_STATE_NONE:
+ case AuthUser::Unchecked:
             /* semantic change: do not drop the connection.
              * 2.5 implementation used to keep it open - Kinkie */
             debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << hdrType << " header: 'NTLM'");
             httpHeaderPutStrf(&rep->header, hdrType, "NTLM");
             break;
 
- case AUTHENTICATE_STATE_IN_PROGRESS:
+ case AuthUser::Handshake:
             /* we're waiting for a response from the client. Pass it the blob */
             debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << hdrType << " header: 'NTLM " << ntlm_request->server_blob << "'");
             httpHeaderPutStrf(&rep->header, hdrType, "NTLM %s", ntlm_request->server_blob);
             safe_free(ntlm_request->server_blob);
             break;
 
-
         default:
- debugs(29, 0, "AuthNTLMConfig::fixHeader: state " << ntlm_request->auth_state << ".");
+ debugs(29, DBG_CRITICAL, "AuthNTLMConfig::fixHeader: state " << ntlm_request->user()->credentials() << ".");
             fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
         }
     }
@@ -323,391 +275,38 @@
     debugs(29, 5, "NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '" << this << "'");
 }
 
-static void
-authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
+int32_t
+NTLMUser::ttl() const
 {
- authenticateStateData *r = static_cast<authenticateStateData *>(data);
-
- int valid;
- char *blob;
-
- AuthUserRequest *auth_user_request;
- AuthUser *auth_user;
- NTLMUser *ntlm_user;
- AuthNTLMUserRequest *ntlm_request;
-
- debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
- valid = cbdataReferenceValid(r->data);
-
- if (!valid) {
- debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. helper '" << lastserver << "'.");
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
- return;
- }
-
- if (!reply) {
- debugs(29, 1, "authenticateNTLMHandleReply: Helper '" << lastserver << "' crashed!.");
- reply = (char *)"BH Internal error";
- }
-
- auth_user_request = r->auth_user_request;
- assert(auth_user_request != NULL);
- ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
-
- assert(ntlm_request != NULL);
- assert(ntlm_request->waiting);
- ntlm_request->waiting = 0;
- safe_free(ntlm_request->client_blob);
-
- auth_user = ntlm_request->user();
- assert(auth_user != NULL);
- assert(auth_user->auth_type == AUTH_NTLM);
- ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user());
-
- assert(ntlm_user != NULL);
-
- if (ntlm_request->authserver == NULL)
- ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
- else
- assert(ntlm_request->authserver == lastserver);
-
- /* seperate out the useful data */
- blob = strchr(reply, ' ');
-
- if (blob)
- blob++;
-
- if (strncasecmp(reply, "TT ", 3) == 0) {
- /* we have been given a blob to send to the client */
- safe_free(ntlm_request->server_blob);
- ntlm_request->request->flags.must_keepalive = 1;
- if (ntlm_request->request->flags.proxy_keepalive) {
- ntlm_request->server_blob = xstrdup(blob);
- ntlm_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
- auth_user_request->denyMessage("Authentication in progress");
- debugs(29, 4, "authenticateNTLMHandleReply: Need to challenge the client with a server blob '" << blob << "'");
- } else {
- ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
- auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
- }
- } else if (strncasecmp(reply, "AF ", 3) == 0) {
- /* we're finished, release the helper */
- ntlm_user->username(blob);
- auth_user_request->denyMessage("Login successful");
- safe_free(ntlm_request->server_blob);
-
- debugs(29, 4, "authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '" << blob << "'");
- /* connection is authenticated */
- debugs(29, 4, "AuthNTLMUserRequest::authenticate: authenticated user " << ntlm_user->username());
- /* see if this is an existing user with a different proxy_auth
- * string */
- auth_user_hash_pointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username()));
- AuthUser *local_auth_user = ntlm_request->user();
- while (usernamehash && (usernamehash->user()->auth_type != AUTH_NTLM || strcmp(usernamehash->user()->username(), ntlm_user->username()) != 0))
- usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
- if (usernamehash) {
- /* we can't seamlessly recheck the username due to the
- * challenge-response nature of the protocol.
- * Just free the temporary auth_user */
- usernamehash->user()->absorb(local_auth_user);
- //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
- local_auth_user = usernamehash->user();
- ntlm_request->_auth_user = local_auth_user;
- } else {
- /* store user in hash's */
- local_auth_user->addToNameCache();
- // authenticateUserNameCacheAdd(local_auth_user);
- }
- /* set these to now because this is either a new login from an
- * existing user or a new user */
- local_auth_user->expiretime = current_time.tv_sec;
- ntlm_request->releaseAuthServer();
- ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
- } else if (strncasecmp(reply, "NA ", 3) == 0) {
- /* authentication failure (wrong password, etc.) */
- auth_user_request->denyMessage(blob);
- ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
- safe_free(ntlm_request->server_blob);
- ntlm_request->releaseAuthServer();
- debugs(29, 4, "authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '" << blob << "'");
- } else if (strncasecmp(reply, "BH ", 3) == 0) {
- /* TODO kick off a refresh process. This can occur after a YR or after
- * a KK. If after a YR release the helper and resubmit the request via
- * Authenticate NTLM start.
- * If after a KK deny the user's request w/ 407 and mark the helper as
- * Needing YR. */
- auth_user_request->denyMessage(blob);
- ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
- safe_free(ntlm_request->server_blob);
- ntlm_request->releaseAuthServer();
- debugs(29, 1, "authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '" << reply << "'");
- } else {
- /* protocol error */
- fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
- }
-
- if (ntlm_request->request) {
- HTTPMSGUNLOCK(ntlm_request->request);
- ntlm_request->request = NULL;
- }
- r->handler(r->data, NULL);
- cbdataReferenceDone(r->data);
- authenticateStateFree(r);
+ return -1; // NTLM credentials cannot be cached.
 }
 
+
 static void
 authenticateNTLMStats(StoreEntry * sentry)
 {
     helperStatefulStats(sentry, ntlmauthenticators, "NTLM Authenticator Statistics");
 }
 
-
-/* send the initial data to a stateful ntlm authenticator module */
-void
-AuthNTLMUserRequest::module_start(RH * handler, void *data)
-{
- authenticateStateData *r = NULL;
- static char buf[8192];
- ntlm_user_t *ntlm_user;
- AuthUser *auth_user = user();
-
- assert(data);
- assert(handler);
- assert(auth_user);
- assert(auth_user->auth_type == AUTH_NTLM);
-
- ntlm_user = dynamic_cast<ntlm_user_t *>(user());
-
- debugs(29, 8, "AuthNTLMUserRequest::module_start: auth state is '" << auth_state << "'");
-
- if (ntlmConfig.authenticate == NULL) {
- debugs(29, 0, "AuthNTLMUserRequest::module_start: no NTLM program specified.");
- handler(data, NULL);
- return;
- }
-
- r = cbdataAlloc(authenticateStateData);
- r->handler = handler;
- r->data = cbdataReference(data);
- r->auth_user_request = this;
- AUTHUSERREQUESTLOCK(r->auth_user_request, "r");
-
- if (auth_state == AUTHENTICATE_STATE_INITIAL) {
- snprintf(buf, 8192, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
- } else {
- snprintf(buf, 8192, "KK %s\n", client_blob);
- }
-
- waiting = 1;
-
- safe_free(client_blob);
- helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
-}
-
-/**
- * Atomic action: properly release the NTLM auth helpers which may have been reserved
- * for this request connections use.
- */
-void
-AuthNTLMUserRequest::releaseAuthServer()
-{
- if (authserver) {
- debugs(29, 6, HERE << "releasing NTLM auth server '" << authserver << "'");
- helperStatefulReleaseServer(authserver);
- authserver = NULL;
- } else
- debugs(29, 6, HERE << "No NTLM auth server to release.");
-}
-
-/* clear any connection related authentication details */
-void
-AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn)
-{
- assert(conn != NULL);
-
- debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
-
- if (conn->auth_user_request == NULL) {
- debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: no auth_user_request");
- return;
- }
-
- // unlock / un-reserve the helpers
- releaseAuthServer();
-
- /* unlock the connection based lock */
- debugs(29, 9, "AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
-
- AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
-}
-
 /*
  * Decode a NTLM [Proxy-]Auth string, placing the results in the passed
  * Auth_user structure.
  */
-AuthUserRequest *
+AuthUserRequest::Pointer
 AuthNTLMConfig::decode(char const *proxy_auth)
 {
- NTLMUser *newUser = new NTLMUser(&ntlmConfig);
- AuthNTLMUserRequest *auth_user_request = new AuthNTLMUserRequest ();
+ NTLMUser *newUser = new NTLMUser(AuthConfig::Find("ntlm"));
+ AuthUserRequest::Pointer auth_user_request = new AuthNTLMUserRequest();
     assert(auth_user_request->user() == NULL);
+
     auth_user_request->user(newUser);
     auth_user_request->user()->auth_type = AUTH_NTLM;
- auth_user_request->user()->addRequest(auth_user_request);
 
     /* all we have to do is identify that it's NTLM - the helper does the rest */
     debugs(29, 9, "AuthNTLMConfig::decode: NTLM authentication");
     return auth_user_request;
 }
 
-int
-AuthNTLMUserRequest::authenticated() const
-{
- if (auth_state == AUTHENTICATE_STATE_DONE) {
- debugs(29, 9, "AuthNTLMUserRequest::authenticated: user authenticated.");
- return 1;
- }
-
- debugs(29, 9, "AuthNTLMUserRequest::authenticated: user not fully authenticated.");
-
- return 0;
-}
-
-void
-AuthNTLMUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
-{
- const char *proxy_auth, *blob;
-
- /* TODO: rename this!! */
- AuthUser *local_auth_user;
- ntlm_user_t *ntlm_user;
-
- local_auth_user = user();
- assert(local_auth_user);
- assert(local_auth_user->auth_type == AUTH_NTLM);
- ntlm_user = dynamic_cast<ntlm_user_t *>(local_auth_user);
- assert (this);
-
- /* Check that we are in the client side, where we can generate
- * auth challenges */
-
- if (conn == NULL || !cbdataReferenceValid(conn)) {
- auth_state = AUTHENTICATE_STATE_FAILED;
- debugs(29, 1, "AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!");
- return;
- }
-
- if (waiting) {
- debugs(29, 1, "AuthNTLMUserRequest::authenticate: waiting for helper reply!");
- return;
- }
-
- if (server_blob) {
- debugs(29, 2, "AuthNTLMUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
- return;
- }
-
- /* get header */
- proxy_auth = aRequest->header.getStr(type);
-
- /* locate second word */
- blob = proxy_auth;
-
- /* if proxy_auth is actually NULL, we'd better not manipulate it. */
- if (blob) {
- while (xisspace(*blob) && *blob)
- blob++;
-
- while (!xisspace(*blob) && *blob)
- blob++;
-
- while (xisspace(*blob) && *blob)
- blob++;
- }
-
- switch (auth_state) {
-
- case AUTHENTICATE_STATE_NONE:
- /* we've received a ntlm request. pass to a helper */
- debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '" << proxy_auth << "'");
- auth_state = AUTHENTICATE_STATE_INITIAL;
- safe_free(client_blob);
- client_blob=xstrdup(blob);
- conn->auth_type = AUTH_NTLM;
- assert(conn->auth_user_request == NULL);
- conn->auth_user_request = this;
- AUTHUSERREQUESTLOCK(conn->auth_user_request, "conn");
- request = aRequest;
- HTTPMSGLOCK(request);
- return;
-
- break;
-
- case AUTHENTICATE_STATE_INITIAL:
- debugs(29, 1, "AuthNTLMUserRequest::authenticate: need to ask helper");
-
- return;
-
- break;
-
-
- case AUTHENTICATE_STATE_IN_PROGRESS:
- /* we should have received a blob from the client. Hand it off to
- * some helper */
- safe_free(client_blob);
-
- client_blob = xstrdup (blob);
-
- if (request)
- HTTPMSGUNLOCK(request);
- request = aRequest;
- HTTPMSGLOCK(request);
- return;
-
- break;
-
- case AUTHENTICATE_STATE_DONE:
- fatal("AuthNTLMUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
-
- break;
-
- case AUTHENTICATE_STATE_FAILED:
- /* we've failed somewhere in authentication */
- debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth);
-
- return;
-
- break;
- }
-
- return;
-}
-
-AuthNTLMUserRequest::AuthNTLMUserRequest() :
- /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE),
- _theUser(NULL)
-{
- waiting=0;
- client_blob=0;
- server_blob=0;
- authserver=NULL;
- request = NULL;
-}
-
-AuthNTLMUserRequest::~AuthNTLMUserRequest()
-{
- safe_free(server_blob);
- safe_free(client_blob);
-
- releaseAuthServer();
-
- if (request) {
- HTTPMSGUNLOCK(request);
- request = NULL;
- }
-}
-
 void
 NTLMUser::deleteSelf() const
 {
@@ -718,15 +317,3 @@
 {
     proxy_auth_list.head = proxy_auth_list.tail = NULL;
 }
-
-AuthConfig *
-ntlmScheme::createConfig()
-{
- return &ntlmConfig;
-}
-
-const char *
-AuthNTLMUserRequest::connLastHeader()
-{
- return NULL;
-}

=== modified file 'src/auth/ntlm/auth_ntlm.h'
--- src/auth/ntlm/auth_ntlm.h 2009-12-16 03:46:59 +0000
+++ src/auth/ntlm/auth_ntlm.h 2010-04-26 07:07:44 +0000
@@ -13,33 +13,17 @@
 
 #define DefaultAuthenticateChildrenMax 32 /* 32 processes */
 
-#ifndef __AUTH_AUTHENTICATE_STATE_T__
-#define __AUTH_AUTHENTICATE_STATE_T__
-typedef enum {
- AUTHENTICATE_STATE_NONE,
- AUTHENTICATE_STATE_INITIAL,
- AUTHENTICATE_STATE_IN_PROGRESS,
- AUTHENTICATE_STATE_DONE,
- AUTHENTICATE_STATE_FAILED
-} auth_state_t; /* connection level auth state */
-
-/* Generic */
-
-typedef struct {
- void *data;
- AuthUserRequest *auth_user_request;
- RH *handler;
-} authenticateStateData;
-#endif
-
 class NTLMUser : public AuthUser
 {
 
 public:
     MEMPROXY_CLASS(NTLMUser);
- virtual void deleteSelf() const;
     NTLMUser(AuthConfig *);
     ~NTLMUser();
+
+ virtual void deleteSelf() const;
+ virtual int32_t ttl() const;
+
     dlink_list proxy_auth_list;
 };
 
@@ -47,57 +31,6 @@
 
 typedef class NTLMUser ntlm_user_t;
 
-class AuthNTLMUserRequest : public AuthUserRequest
-{
-
-public:
- MEMPROXY_CLASS(AuthNTLMUserRequest);
-
- AuthNTLMUserRequest();
- virtual ~AuthNTLMUserRequest();
- virtual int authenticated() const;
- virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
- virtual int module_direction();
- virtual void onConnectionClose(ConnStateData *);
- virtual void module_start(RH *, void *);
- virtual AuthUser *user() {return _theUser;}
-
- virtual const AuthUser *user() const {return _theUser;}
-
- virtual void user (AuthUser *aUser) {_theUser=dynamic_cast<NTLMUser *>(aUser);}
-
- virtual const char * connLastHeader();
-
- /* we need to store the helper server between requests */
- helper_stateful_server *authserver;
- void releaseAuthServer(void); ///< Release authserver NTLM helpers properly when finished or abandoning.
-
- /* what connection is this associated with */
-// ConnStateData * conn;
-
- /* how far through the authentication process are we? */
- auth_state_t auth_state;
-
- /* our current blob to pass to the client */
- char *server_blob;
- /* our current blob to pass to the server */
- char *client_blob;
-
- /* currently waiting for helper response */
- unsigned char waiting;
-
- /* need access to the request flags to mess around on pconn failure */
- HttpRequest *request;
-
-private:
- /* the user */
- NTLMUser * _theUser;
-};
-
-MEMPROXY_CLASS_INLINE(AuthNTLMUserRequest);
-
-#include "HelperChildConfig.h"
-
 /* configuration runtime data */
 
 class AuthNTLMConfig : public AuthConfig
@@ -107,19 +40,20 @@
     AuthNTLMConfig();
     virtual bool active() const;
     virtual bool configured() const;
- virtual AuthUserRequest *decode(char const *proxy_auth);
+ virtual AuthUserRequest::Pointer decode(char const *proxy_auth);
     virtual void done();
+ virtual void rotateHelpers();
     virtual void dump(StoreEntry *, const char *, AuthConfig *);
- virtual void fixHeader(AuthUserRequest *, HttpReply *, http_hdr_type, HttpRequest *);
+ virtual void fixHeader(AuthUserRequest::Pointer, HttpReply *, http_hdr_type, HttpRequest *);
     virtual void init(AuthConfig *);
     virtual void parse(AuthConfig *, int, char *);
     virtual void registerWithCacheManager(void);
     virtual const char * type() const;
- HelperChildConfig authenticateChildren;
     int keep_alive;
- wordlist *authenticate;
 };
 
 typedef class AuthNTLMConfig auth_ntlm_config;
 
+extern statefulhelper *ntlmauthenticators;
+
 #endif

=== modified file 'src/auth/ntlm/ntlmScheme.cc'
--- src/auth/ntlm/ntlmScheme.cc 2009-05-20 06:59:04 +0000
+++ src/auth/ntlm/ntlmScheme.cc 2010-01-17 11:58:04 +0000
@@ -1,4 +1,3 @@
-
 /*
  * $Id$
  *
@@ -31,19 +30,19 @@
  *
  */
 
-#include "ntlmScheme.h"
+#include "config.h"
+#include "auth/ntlm/auth_ntlm.h"
+#include "auth/ntlm/ntlmScheme.h"
+#include "helper.h"
 
-AuthScheme &
+AuthScheme::Pointer
 ntlmScheme::GetInstance()
 {
- if (_instance == NULL)
+ if (_instance == NULL) {
         _instance = new ntlmScheme();
- return *_instance;
-}
-
-ntlmScheme::ntlmScheme()
-{
- AddScheme(*this);
+ AddScheme(_instance);
+ }
+ return _instance;
 }
 
 char const *
@@ -52,4 +51,20 @@
     return "ntlm";
 }
 
-ntlmScheme *ntlmScheme::_instance = NULL;
+AuthScheme::Pointer ntlmScheme::_instance = NULL;
+
+void
+ntlmScheme::done()
+{
+ /* clear the global handle to this scheme. */
+ _instance = NULL;
+
+ debugs(29, 2, "ntlmScheme::done: NTLM authentication Shutdown.");
+}
+
+AuthConfig *
+ntlmScheme::createConfig()
+{
+ auth_ntlm_config *ntlmCfg = new auth_ntlm_config;
+ return dynamic_cast<AuthConfig*>(ntlmCfg);
+}

=== modified file 'src/auth/ntlm/ntlmScheme.h'
--- src/auth/ntlm/ntlmScheme.h 2009-05-20 06:59:04 +0000
+++ src/auth/ntlm/ntlmScheme.h 2010-01-17 11:58:04 +0000
@@ -35,6 +35,7 @@
 #define SQUID_NTLMSCHEME_H
 
 #include "auth/Scheme.h"
+#include "auth/ntlm/auth_ntlm.h"
 
 /// \ingroup AuthSchemeAPI
 /// \ingroup AuthAPI
@@ -42,20 +43,25 @@
 {
 
 public:
- static AuthScheme &GetInstance();
- ntlmScheme();
+ static AuthScheme::Pointer GetInstance();
+ ntlmScheme() {};
     virtual ~ntlmScheme() {};
 
     /* per scheme */
     virtual char const *type () const;
     virtual void done();
     virtual AuthConfig *createConfig();
+
     /* Not implemented */
     ntlmScheme (ntlmScheme const &);
     ntlmScheme &operator=(ntlmScheme const &);
 
 private:
- static ntlmScheme *_instance;
+ /**
+ * Main instance of this authentication Scheme.
+ * NULL when the scheme is not being used.
+ */
+ static AuthScheme::Pointer _instance;
 };
 
 #endif /* SQUID_ntlmSCHEME_H */

=== added file 'src/auth/ntlm/ntlmUserRequest.cc'
--- src/auth/ntlm/ntlmUserRequest.cc 1970-01-01 00:00:00 +0000
+++ src/auth/ntlm/ntlmUserRequest.cc 2010-05-06 11:07:19 +0000
@@ -0,0 +1,358 @@
+#include "config.h"
+#include "auth/ntlm/ntlmUserRequest.h"
+#include "auth/ntlm/auth_ntlm.h"
+#include "auth/State.h"
+#include "cbdata.h"
+#include "HttpRequest.h"
+#include "SquidTime.h"
+
+/* state wrapper functions */
+
+AuthNTLMUserRequest::AuthNTLMUserRequest()
+{
+ waiting=0;
+ client_blob=0;
+ server_blob=0;
+ authserver=NULL;
+ request = NULL;
+}
+
+AuthNTLMUserRequest::~AuthNTLMUserRequest()
+{
+ assert(RefCountCount()==0);
+ safe_free(server_blob);
+ safe_free(client_blob);
+
+ releaseAuthServer();
+
+ if (request) {
+ HTTPMSGUNLOCK(request);
+ request = NULL;
+ }
+}
+
+const char *
+AuthNTLMUserRequest::connLastHeader()
+{
+ return NULL;
+}
+
+/* See AuthUserRequest.cc::authenticateDirection for return values */
+int
+AuthNTLMUserRequest::module_direction()
+{
+ /* null auth_user is checked for by authenticateDirection */
+
+ if (waiting || client_blob)
+ return -1; /* need helper response to continue */
+
+ if (user()->auth_type != AUTH_NTLM)
+ return -2;
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Handshake:
+ assert(server_blob);
+ return 1; /* send to client */
+
+ case AuthUser::Ok:
+ return 0; /* do nothing */
+
+ case AuthUser::Failed:
+ return -2;
+
+ default:
+ debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication in unexpected state: " << user()->credentials());
+ return -2;
+ }
+}
+
+/* send the initial data to a stateful ntlm authenticator module */
+void
+AuthNTLMUserRequest::module_start(RH * handler, void *data)
+{
+ authenticateStateData *r = NULL;
+ static char buf[8192];
+
+ assert(data);
+ assert(handler);
+
+ debugs(29, 8, HERE << "credentials state is '" << user()->credentials() << "'");
+
+ if (static_cast<AuthNTLMConfig*>(AuthConfig::Find("ntlm"))->authenticate == NULL) {
+ debugs(29, DBG_CRITICAL, "ERROR: NTLM Start: no NTLM program configured.");
+ handler(data, NULL);
+ return;
+ }
+
+ r = cbdataAlloc(authenticateStateData);
+ r->handler = handler;
+ r->data = cbdataReference(data);
+ r->auth_user_request = this;
+
+ if (user()->credentials() == AuthUser::Pending) {
+ snprintf(buf, 8192, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
+ } else {
+ snprintf(buf, 8192, "KK %s\n", client_blob);
+ }
+
+ waiting = 1;
+
+ safe_free(client_blob);
+ helperStatefulSubmit(ntlmauthenticators, buf, AuthNTLMUserRequest::HandleReply, r, authserver);
+}
+
+/**
+ * Atomic action: properly release the NTLM auth helpers which may have been reserved
+ * for this request connections use.
+ */
+void
+AuthNTLMUserRequest::releaseAuthServer()
+{
+ if (authserver) {
+ debugs(29, 6, HERE << "releasing NTLM auth server '" << authserver << "'");
+ helperStatefulReleaseServer(authserver);
+ authserver = NULL;
+ } else
+ debugs(29, 6, HERE << "No NTLM auth server to release.");
+}
+
+void
+AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn)
+{
+ assert(conn != NULL);
+
+ debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
+
+ if (conn->auth_user_request == NULL) {
+ debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: no auth_user_request");
+ return;
+ }
+
+ // unlock / un-reserve the helpers
+ releaseAuthServer();
+
+ /* unlock the connection based lock */
+ debugs(29, 9, "AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
+
+ conn->auth_user_request = NULL;
+}
+
+int
+AuthNTLMUserRequest::authenticated() const
+{
+ if (user()->credentials() == AuthUser::Ok) {
+ debugs(29, 9, "AuthNTLMUserRequest::authenticated: user authenticated.");
+ return 1;
+ }
+
+ debugs(29, 9, "AuthNTLMUserRequest::authenticated: user not fully authenticated.");
+
+ return 0;
+}
+
+void
+AuthNTLMUserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
+{
+ const char *proxy_auth, *blob;
+
+ assert(this);
+
+ /* Check that we are in the client side, where we can generate
+ * auth challenges */
+
+ if (conn == NULL || !cbdataReferenceValid(conn)) {
+ user()->credentials(AuthUser::Failed);
+ debugs(29, 1, "AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!");
+ return;
+ }
+
+ if (waiting) {
+ debugs(29, 1, "AuthNTLMUserRequest::authenticate: waiting for helper reply!");
+ return;
+ }
+
+ if (server_blob) {
+ debugs(29, 2, "AuthNTLMUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
+ return;
+ }
+
+ /* get header */
+ proxy_auth = aRequest->header.getStr(type);
+
+ /* locate second word */
+ blob = proxy_auth;
+
+ /* if proxy_auth is actually NULL, we'd better not manipulate it. */
+ if (blob) {
+ while (xisspace(*blob) && *blob)
+ blob++;
+
+ while (!xisspace(*blob) && *blob)
+ blob++;
+
+ while (xisspace(*blob) && *blob)
+ blob++;
+ }
+
+ switch (user()->credentials()) {
+
+ case AuthUser::Unchecked:
+ /* we've received a ntlm request. pass to a helper */
+ debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '" << proxy_auth << "'");
+ user()->credentials(AuthUser::Pending);
+ safe_free(client_blob);
+ client_blob=xstrdup(blob);
+ assert(conn->auth_user_request == NULL);
+ conn->auth_user_request = this;
+ request = aRequest;
+ HTTPMSGLOCK(request);
+ break;
+
+ case AuthUser::Pending:
+ debugs(29, 1, "AuthNTLMUserRequest::authenticate: need to ask helper");
+ break;
+
+ case AuthUser::Handshake:
+ /* we should have received a blob from the client. Hand it off to
+ * some helper */
+ safe_free(client_blob);
+ client_blob = xstrdup (blob);
+
+ if (request)
+ HTTPMSGUNLOCK(request);
+ request = aRequest;
+ HTTPMSGLOCK(request);
+ break;
+
+ case AuthUser::Ok:
+ fatal("AuthNTLMUserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
+ break;
+
+ case AuthUser::Failed:
+ /* we've failed somewhere in authentication */
+ debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth);
+ break;
+ }
+}
+
+void
+AuthNTLMUserRequest::HandleReply(void *data, void *lastserver, char *reply)
+{
+ authenticateStateData *r = static_cast<authenticateStateData *>(data);
+
+ int valid;
+ char *blob;
+
+ debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
+ valid = cbdataReferenceValid(r->data);
+
+ if (!valid) {
+ debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. helper '" << lastserver << "'.");
+ cbdataReferenceDone(r->data);
+ authenticateStateFree(r);
+ return;
+ }
+
+ if (!reply) {
+ debugs(29, 1, "authenticateNTLMHandleReply: Helper '" << lastserver << "' crashed!.");
+ reply = (char *)"BH Internal error";
+ }
+
+ AuthUserRequest::Pointer auth_user_request = r->auth_user_request;
+ assert(auth_user_request != NULL);
+
+ AuthNTLMUserRequest *ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request.getRaw());
+ assert(ntlm_request != NULL);
+ assert(ntlm_request->waiting);
+ assert(ntlm_request->user() != NULL);
+ assert(ntlm_request->user()->auth_type == AUTH_NTLM);
+
+ ntlm_request->waiting = 0;
+ safe_free(ntlm_request->client_blob);
+
+ if (ntlm_request->authserver == NULL)
+ ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
+ else
+ assert(ntlm_request->authserver == lastserver);
+
+ /* seperate out the useful data */
+ blob = strchr(reply, ' ');
+ if (blob)
+ blob++;
+
+ if (strncasecmp(reply, "TT ", 3) == 0) {
+ /* we have been given a blob to send to the client */
+ safe_free(ntlm_request->server_blob);
+ ntlm_request->request->flags.must_keepalive = 1;
+ if (ntlm_request->request->flags.proxy_keepalive) {
+ ntlm_request->server_blob = xstrdup(blob);
+ ntlm_request->user()->credentials(AuthUser::Handshake);
+ auth_user_request->denyMessage("Authentication in progress");
+ debugs(29, 4, "authenticateNTLMHandleReply: Need to challenge the client with a server blob '" << blob << "'");
+ } else {
+ ntlm_request->user()->credentials(AuthUser::Failed);
+ auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
+ }
+ } else if (strncasecmp(reply, "AF ", 3) == 0) {
+ /* we're finished, release the helper */
+ auth_user_request->user()->username(blob);
+ auth_user_request->denyMessage("Login successful");
+ safe_free(ntlm_request->server_blob);
+
+ debugs(29, 4, "authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '" << blob << "'");
+ /* connection is authenticated */
+ debugs(29, 4, "AuthNTLMUserRequest::authenticate: authenticated user " << auth_user_request->user()->username());
+ /* see if this is an existing user with a different proxy_auth
+ * string */
+ auth_user_hash_pointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username()));
+ AuthUser::Pointer local_auth_user = ntlm_request->user();
+ while (usernamehash && (usernamehash->user()->auth_type != AUTH_NTLM || strcmp(usernamehash->user()->username(), auth_user_request->user()->username()) != 0))
+ usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
+ if (usernamehash) {
+ /* we can't seamlessly recheck the username due to the
+ * challenge-response nature of the protocol.
+ * Just free the temporary auth_user */
+ usernamehash->user()->absorb(local_auth_user);
+ local_auth_user = usernamehash->user();
+ ntlm_request->_auth_user = local_auth_user;
+ } else {
+ /* store user in hash's */
+ local_auth_user->addToNameCache();
+ }
+ /* set these to now because this is either a new login from an
+ * existing user or a new user */
+ local_auth_user->expiretime = current_time.tv_sec;
+ ntlm_request->releaseAuthServer();
+ local_auth_user->credentials(AuthUser::Ok);
+ } else if (strncasecmp(reply, "NA ", 3) == 0) {
+ /* authentication failure (wrong password, etc.) */
+ auth_user_request->denyMessage(blob);
+ ntlm_request->user()->credentials(AuthUser::Failed);
+ safe_free(ntlm_request->server_blob);
+ ntlm_request->releaseAuthServer();
+ debugs(29, 4, "authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '" << blob << "'");
+ } else if (strncasecmp(reply, "BH ", 3) == 0) {
+ /* TODO kick off a refresh process. This can occur after a YR or after
+ * a KK. If after a YR release the helper and resubmit the request via
+ * Authenticate NTLM start.
+ * If after a KK deny the user's request w/ 407 and mark the helper as
+ * Needing YR. */
+ auth_user_request->denyMessage(blob);
+ auth_user_request->user()->credentials(AuthUser::Failed);
+ safe_free(ntlm_request->server_blob);
+ ntlm_request->releaseAuthServer();
+ debugs(29, 1, "authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '" << reply << "'");
+ } else {
+ /* protocol error */
+ fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
+ }
+
+ if (ntlm_request->request) {
+ HTTPMSGUNLOCK(ntlm_request->request);
+ ntlm_request->request = NULL;
+ }
+ r->handler(r->data, NULL);
+ cbdataReferenceDone(r->data);
+ authenticateStateFree(r);
+}

=== added file 'src/auth/ntlm/ntlmUserRequest.h'
--- src/auth/ntlm/ntlmUserRequest.h 1970-01-01 00:00:00 +0000
+++ src/auth/ntlm/ntlmUserRequest.h 2010-05-06 11:07:19 +0000
@@ -0,0 +1,54 @@
+#ifndef _SQUID_SRC_AUTH_NTLM_USERREQUEST_H
+#define _SQUID_SRC_AUTH_NTLM_USERREQUEST_H
+
+#include "auth/UserRequest.h"
+#include "auth/ntlm/auth_ntlm.h"
+#include "MemPool.h"
+
+class ConnStateData;
+class HttpReply;
+class HttpRequest;
+struct helper_stateful_server;
+
+class AuthNTLMUserRequest : public AuthUserRequest
+{
+
+public:
+ MEMPROXY_CLASS(AuthNTLMUserRequest);
+
+ AuthNTLMUserRequest();
+ virtual ~AuthNTLMUserRequest();
+ virtual int authenticated() const;
+ virtual void authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type);
+ virtual int module_direction();
+ virtual void onConnectionClose(ConnStateData *);
+ virtual void module_start(RH *, void *);
+
+ virtual const char * connLastHeader();
+
+ /* we need to store the helper server between requests */
+ helper_stateful_server *authserver;
+ void releaseAuthServer(void); ///< Release authserver NTLM helpers properly when finished or abandoning.
+
+ /* what connection is this associated with */
+// ConnStateData * conn;
+
+ /* our current blob to pass to the client */
+ char *server_blob;
+
+ /* our current blob to pass to the server */
+ char *client_blob;
+
+ /* currently waiting for helper response */
+ unsigned char waiting;
+
+ /* need access to the request flags to mess around on pconn failure */
+ HttpRequest *request;
+
+private:
+ static HLPSCB HandleReply;
+};
+
+MEMPROXY_CLASS_INLINE(AuthNTLMUserRequest);
+
+#endif /* _SQUID_SRC_AUTH_NTLM_USERREQUEST_H */

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc 2010-05-02 19:32:42 +0000
+++ src/cache_cf.cc 2010-05-13 06:20:23 +0000
@@ -398,6 +398,8 @@
     int err_count = 0;
     CacheManager *manager=CacheManager::GetInstance();
 
+ debugs(5, 4, HERE);
+
     configFreeMemory();
 
     ACLMethodData::ThePurgeCount = 0;
@@ -1436,7 +1438,7 @@
 }
 
 static void
-parse_authparam(authConfig * config)
+parse_authparam(Auth::authConfig * config)
 {
     char *type_str;
     char *param_str;
@@ -1447,38 +1449,43 @@
     if ((param_str = strtok(NULL, w_space)) == NULL)
         self_destruct();
 
- /* find a configuration for the scheme */
- AuthConfig *scheme = AuthConfig::Find (type_str);
-
- if (scheme == NULL) {
- /* Create a configuration */
- AuthScheme *theScheme;
-
- if ((theScheme = AuthScheme::Find(type_str)) == NULL) {
- debugs(3, 0, "Parsing Config File: Unknown authentication scheme '" << type_str << "'.");
- return;
+ /* find a configuration for the scheme in the currently parsed configs... */
+ AuthConfig *schemeCfg = AuthConfig::Find(type_str);
+
+ if (schemeCfg == NULL) {
+ /* Create a configuration based on the scheme info */
+ AuthScheme::Pointer theScheme = AuthScheme::Find(type_str);
+
+ if (theScheme == NULL) {
+ debugs(3, DBG_CRITICAL, "Parsing Config File: Unknown authentication scheme '" << type_str << "'.");
+ self_destruct();
         }
 
         config->push_back(theScheme->createConfig());
- scheme = config->back();
- assert (scheme);
+ schemeCfg = AuthConfig::Find(type_str);
+ if (schemeCfg == NULL) {
+ debugs(3, DBG_CRITICAL, "Parsing Config File: Corruption configuring authentication scheme '" << type_str << "'.");
+ self_destruct();
+ }
     }
 
- scheme->parse(scheme, config->size(), param_str);
+ schemeCfg->parse(schemeCfg, config->size(), param_str);
 }
 
 static void
-free_authparam(authConfig * cfg)
+free_authparam(Auth::authConfig * cfg)
 {
- AuthConfig *scheme;
- /* DON'T FREE THESE FOR RECONFIGURE */
-
- if (reconfiguring)
- return;
-
+ /* Wipe the Auth globals and Detach/Destruct component config + state. */
+ cfg->clean();
+
+ /* remove our pointers to the probably-dead sub-configs */
     while (cfg->size()) {
- scheme = cfg->pop_back();
- scheme->done();
+ cfg->pop_back();
+ }
+
+ /* on reconfigure initialize new auth schemes for the new config. */
+ if(reconfiguring) {
+ InitAuthSchemes();
     }
 }
 

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre 2010-05-14 05:37:19 +0000
+++ src/cf.data.pre 2010-05-23 12:00:47 +0000
@@ -66,7 +66,7 @@
 
 NAME: auth_param
 TYPE: authparam
-LOC: Config.authConfiguration
+LOC: Auth::TheConfig
 DEFAULT: none
 DOC_START
         This is used to define parameters for the various authentication

=== modified file 'src/cf_gen.cc'
--- src/cf_gen.cc 2010-05-02 18:51:26 +0000
+++ src/cf_gen.cc 2010-05-13 06:20:23 +0000
@@ -711,6 +711,7 @@
             "static void\n"
             "dump_config(StoreEntry *entry)\n"
             "{\n"
+ " debugs(5, 4, HERE);\n"
            );
 
     for (entry = head; entry != NULL; entry = entry->next) {
@@ -745,6 +746,7 @@
             "static void\n"
             "free_all(void)\n"
             "{\n"
+ " debugs(5, 4, HERE);\n"
            );
 
     for (entry = head; entry != NULL; entry = entry->next) {

=== modified file 'src/client_side.cc'
--- src/client_side.cc 2010-05-02 19:32:42 +0000
+++ src/client_side.cc 2010-05-13 06:20:23 +0000
@@ -499,13 +499,12 @@
         aLogEntry->cache.requestSize += request->content_length;
     aLogEntry->cache.extuser = request->extacl_user.termedBuf();
 
- if (request->auth_user_request) {
+ if (request->auth_user_request != NULL) {
 
         if (request->auth_user_request->username())
- aLogEntry->cache.authuser =
- xstrdup(request->auth_user_request->username());
+ aLogEntry->cache.authuser = xstrdup(request->auth_user_request->username());
 
- AUTHUSERREQUESTUNLOCK(request->auth_user_request, "request via clientPrepareLogWithRequestDetails");
+// WTF?? request->auth_user_request = NULL;
     }
 }
 
@@ -691,8 +690,6 @@
     if (!flags.swanSang)
         debugs(33, 1, "BUG: ConnStateData was not destroyed properly; FD " << fd);
 
- AUTHUSERREQUESTUNLOCK(auth_user_request, "~conn");
-
     cbdataReferenceDone(port);
 
     if (bodyPipe != NULL)

=== modified file 'src/client_side.h'
--- src/client_side.h 2010-04-17 02:29:04 +0000
+++ src/client_side.h 2010-05-04 12:33:30 +0000
@@ -33,6 +33,7 @@
 #ifndef SQUID_CLIENTSIDE_H
 #define SQUID_CLIENTSIDE_H
 
+#include "auth/UserRequest.h"
 #include "base/AsyncJob.h"
 #include "BodyPipe.h"
 #include "comm.h"
@@ -43,13 +44,8 @@
 #include "StoreIOBuffer.h"
 
 class ConnStateData;
-
 class ClientHttpRequest;
-
 class clientStreamNode;
-
-class AuthUserRequest;
-
 class ChunkedCodingParser;
 class HttpParser;
 
@@ -174,16 +170,10 @@
     int64_t bodySizeLeft();
 
     /**
- * Is this connection based authentication? if so what type it
- * is.
- */
- auth_type_t auth_type;
-
- /**
- * note this is ONLY connection based because NTLM is against HTTP spec.
+ * note this is ONLY connection based because NTLM and Negotiate is against HTTP spec.
      * the user details for connection based authentication
      */
- AuthUserRequest *auth_user_request;
+ AuthUserRequest::Pointer auth_user_request;
 
     /**
      * used by the owner of the connection, opaque otherwise

=== modified file 'src/client_side_reply.cc'
--- src/client_side_reply.cc 2010-04-17 02:29:04 +0000
+++ src/client_side_reply.cc 2010-04-17 10:38:50 +0000
@@ -96,10 +96,9 @@
 clientReplyContext::setReplyToError(
     err_type err, http_status status, const HttpRequestMethod& method, char const *uri,
     Ip::Address &addr, HttpRequest * failedrequest, const char *unparsedrequest,
- AuthUserRequest * auth_user_request)
+ AuthUserRequest::Pointer auth_user_request)
 {
- ErrorState *errstate =
- clientBuildError(err, status, uri, addr, failedrequest);
+ ErrorState *errstate = clientBuildError(err, status, uri, addr, failedrequest);
 
     if (unparsedrequest)
         errstate->request_hdrs = xstrdup(unparsedrequest);
@@ -112,10 +111,7 @@
 
     createStoreEntry(method, request_flags());
 
- if (auth_user_request) {
- errstate->auth_user_request = auth_user_request;
- AUTHUSERREQUESTLOCK(errstate->auth_user_request, "errstate");
- }
+ errstate->auth_user_request = auth_user_request;
 
     assert(errstate->callback_data == NULL);
     errorAppendEntry(http->storeEntry(), errstate);
@@ -1355,9 +1351,8 @@
          * responses
          */
         authenticateFixHeader(reply, request->auth_user_request, request, 0, 1);
- } else if (request->auth_user_request)
- authenticateFixHeader(reply, request->auth_user_request, request,
- http->flags.accel, 0);
+ } else if (request->auth_user_request != NULL)
+ authenticateFixHeader(reply, request->auth_user_request, request, http->flags.accel, 0);
 
     /* Append X-Cache */
     httpHeaderPutStrf(hdr, HDR_X_CACHE, "%s from %s",

=== modified file 'src/client_side_reply.h'
--- src/client_side_reply.h 2010-05-02 18:52:45 +0000
+++ src/client_side_reply.h 2010-05-13 06:20:23 +0000
@@ -72,7 +72,7 @@
     int storeOKTransferDone() const;
     int storeNotOKTransferDone() const;
 
- void setReplyToError(err_type, http_status, const HttpRequestMethod&, char const *, Ip::Address &, HttpRequest *, const char *, AuthUserRequest *);
+ void setReplyToError(err_type, http_status, const HttpRequestMethod&, char const *, Ip::Address &, HttpRequest *, const char *, AuthUserRequest::Pointer);
     void createStoreEntry(const HttpRequestMethod& m, request_flags flags);
     void removeStoreReference(store_client ** scp, StoreEntry ** ep);
     void removeClientStoreReference(store_client **scp, ClientHttpRequest *http);

=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc 2010-05-13 15:08:27 +0000
+++ src/client_side_request.cc 2010-05-23 12:00:47 +0000
@@ -623,7 +623,7 @@
                                     http->getConn() != NULL ? http->getConn()->peer : tmpnoaddr,
                                     http->request,
                                     NULL,
- http->getConn() != NULL && http->getConn()->auth_user_request ?
+ http->getConn() != NULL && http->getConn()->auth_user_request != NULL ?
                                     http->getConn()->auth_user_request : http->request->auth_user_request);
 
         node = (clientStreamNode *)http->client_stream.tail->data;
@@ -1038,11 +1038,7 @@
         new_request->my_addr = old_request->my_addr;
         new_request->flags = old_request->flags;
         new_request->flags.redirected = 1;
-
- if (old_request->auth_user_request) {
- new_request->auth_user_request = old_request->auth_user_request;
- AUTHUSERREQUESTLOCK(new_request->auth_user_request, "new request");
- }
+ new_request->auth_user_request = old_request->auth_user_request;
 
         if (old_request->body_pipe != NULL) {
             new_request->body_pipe = old_request->body_pipe;
@@ -1524,7 +1520,7 @@
     repContext->setReplyToError(ERR_ICAP_FAILURE, HTTP_INTERNAL_SERVER_ERROR,
                                 request->method, NULL,
                                 (c != NULL ? c->peer : noAddr), request, NULL,
- (c != NULL && c->auth_user_request ?
+ (c != NULL && c->auth_user_request != NULL ?
                                  c->auth_user_request : request->auth_user_request));
 
     node = (clientStreamNode *)client_stream.tail->data;

=== modified file 'src/enums.h'
--- src/enums.h 2010-01-01 21:16:57 +0000
+++ src/enums.h 2010-01-17 12:12:50 +0000
@@ -199,22 +199,6 @@
     STREAM_FAILED
 } clientStream_status_t;
 
-typedef enum {
- AUTH_ACL_CHALLENGE = -2,
- AUTH_ACL_HELPER = -1,
- AUTH_ACL_CANNOT_AUTHENTICATE = 0,
- AUTH_AUTHENTICATED = 1
-} auth_acl_t;
-
-typedef enum {
- AUTH_UNKNOWN, /* default */
- AUTH_BASIC,
- AUTH_NTLM,
- AUTH_DIGEST,
- AUTH_NEGOTIATE,
- AUTH_BROKEN /* known type, but broken data */
-} auth_type_t;
-
 /* stateful helper callback response codes */
 typedef enum {
     S_HELPER_UNKNOWN,

=== modified file 'src/errorpage.cc'
--- src/errorpage.cc 2010-02-26 01:13:20 +0000
+++ src/errorpage.cc 2010-03-06 00:12:22 +0000
@@ -501,7 +501,7 @@
     wordlistDestroy(&err->ftp.server_msg);
     safe_free(err->ftp.request);
     safe_free(err->ftp.reply);
- AUTHUSERREQUESTUNLOCK(err->auth_user_request, "errstate");
+ err->auth_user_request = NULL;
     safe_free(err->err_msg);
 #if USE_ERR_LOCALES
     if (err->err_language != Config.errorDefaultLanguage)
@@ -609,7 +609,7 @@
     switch (token) {
 
     case 'a':
- if (request && request->auth_user_request)
+ if (request && request->auth_user_request != NULL)
             p = request->auth_user_request->username();
         if (!p)
             p = "-";

=== modified file 'src/errorpage.h'
--- src/errorpage.h 2010-05-02 19:32:42 +0000
+++ src/errorpage.h 2010-05-13 06:20:23 +0000
@@ -35,6 +35,7 @@
 #define SQUID_ERRORPAGE_H
 
 #include "squid.h"
+#include "auth/UserRequest.h"
 #include "cbdata.h"
 #include "ip/Address.h"
 
@@ -78,7 +79,6 @@
  \endverbatim
  */
 
-class AuthUserRequest;
 class HttpReply;
 class MemBuf;
 
@@ -127,7 +127,7 @@
     int page_id;
     char *err_language;
     http_status httpStatus;
- AuthUserRequest *auth_user_request;
+ AuthUserRequest::Pointer auth_user_request;
     HttpRequest *request;
     char *url;
     int xerrno;

=== modified file 'src/external_acl.cc'
--- src/external_acl.cc 2010-04-17 02:29:04 +0000
+++ src/external_acl.cc 2010-04-17 10:38:50 +0000
@@ -121,6 +121,12 @@
 
     dlink_list queue;
 
+ /**
+ * Configuration flag. May only be altered by the configuration parser.
+ *
+ * Indicates that all uses of this external_acl_type helper require authentication
+ * details to be processed. If none are available its a fail match.
+ */
     bool require_auth;
 
     enum {
@@ -741,18 +747,17 @@
         if (acl->def->require_auth) {
             int ti;
             /* Make sure the user is authenticated */
+ debugs(82, 3, "aclMatchExternal: " << acl->def->name << " check user authenticated.");
 
             if ((ti = AuthenticateAcl(ch)) != 1) {
                 debugs(82, 2, "aclMatchExternal: " << acl->def->name << " user not authenticated (" << ti << ")");
                 return ti;
             }
+ debugs(82, 3, "aclMatchExternal: " << acl->def->name << " user is authenticated.");
         }
 
         key = makeExternalAclKey(ch, acl);
 
- if (acl->def->require_auth)
- AUTHUSERREQUESTUNLOCK(ch->auth_user_request, "ACLChecklist via aclMatchExternal");
-
         if (!key) {
             /* Not sufficient data to process */
             return -1;
@@ -869,7 +874,7 @@
         switch (format->type) {
 
         case _external_acl_format::EXT_ACL_LOGIN:
- assert (ch->auth_user_request);
+ assert (ch->auth_user_request != NULL);
             str = ch->auth_user_request->username();
             break;
 #if USE_IDENT
@@ -1282,6 +1287,7 @@
     if (acl->def->require_auth) {
         int ti;
         /* Make sure the user is authenticated */
+ debugs(82, 3, "aclMatchExternal: " << acl->def->name << " check user authenticated.");
 
         if ((ti = AuthenticateAcl(ch)) != 1) {
             debugs(82, 1, "externalAclLookup: " << acl->def->name <<
@@ -1289,6 +1295,7 @@
             callback(callback_data, NULL);
             return;
         }
+ debugs(82, 3, "aclMatchExternal: " << acl->def->name << " user is authenticated.");
     }
 
     const char *key = makeExternalAclKey(ch, acl);

=== modified file 'src/http.cc'
--- src/http.cc 2010-05-22 03:55:41 +0000
+++ src/http.cc 2010-05-23 12:00:47 +0000
@@ -1529,7 +1529,7 @@
 
         if (orig_request->extacl_user.size())
             username = orig_request->extacl_user.termedBuf();
- else if (orig_request->auth_user_request)
+ else if (orig_request->auth_user_request != NULL)
             username = orig_request->auth_user_request->username();
 
         snprintf(loginbuf, sizeof(loginbuf), "%s%s", username, orig_request->peer_login + 1);

=== modified file 'src/main.cc'
--- src/main.cc 2010-03-31 15:59:21 +0000
+++ src/main.cc 2010-04-26 07:07:44 +0000
@@ -212,7 +212,12 @@
     WIN32_svcstatusupdate(SERVICE_STOP_PENDING, (wait + 1) * 1000);
 #endif
 
+ /* run the closure code which can be shared with reconfigure */
     serverConnectionsClose();
+
+ /* detach the auth components (only do this on full shutdown) */
+ AuthScheme::FreeAll();
+
     eventAdd("SquidShutdown", &StopEventLoop, this, (double) (wait + 1), 1, false);
 }
 
@@ -693,7 +698,7 @@
 #endif
 
     redirectShutdown();
- authenticateShutdown();
+ authenticateReset();
     externalAclShutdown();
     storeDirCloseSwapLogs();
     storeLogClose();
@@ -731,7 +736,6 @@
     setEffectiveUser();
     _db_init(Debug::cache_log, Debug::debugOptions);
     ipcache_restart(); /* clear stuck entries */
- authenticateUserCacheRestart(); /* clear stuck ACL entries */
     fqdncache_restart(); /* sigh, fqdncache too */
     parseEtcHosts();
     errorInitialize(); /* reload error pages */
@@ -751,7 +755,7 @@
 #endif
 
     redirectInit();
- authenticateInit(&Config.authConfiguration);
+ authenticateInit(&Auth::TheConfig);
     externalAclInit();
 #if USE_WCCP
 
@@ -793,7 +797,7 @@
     dnsShutdown();
 #endif
     redirectShutdown();
- authenticateShutdown();
+ authenticateRotate();
     externalAclShutdown();
 
     _db_rotate_log(); /* cache.log */
@@ -814,7 +818,7 @@
     dnsInit();
 #endif
     redirectInit();
- authenticateInit(&Config.authConfiguration);
+ authenticateInit(&Auth::TheConfig);
     externalAclInit();
 }
 
@@ -947,7 +951,7 @@
 
     redirectInit();
 
- authenticateInit(&Config.authConfiguration);
+ authenticateInit(&Auth::TheConfig);
 
     externalAclInit();
 
@@ -1263,6 +1267,8 @@
         /* we may want the parsing process to set this up in the future */
         Store::Root(new StoreController);
 
+ InitAuthSchemes(); /* required for config parsing */
+
         parse_err = parseConfigFile(ConfigFile);
 
         Mem::Report();
@@ -1717,7 +1723,7 @@
     DelayPools::FreePools();
 #endif
 
- authenticateShutdown();
+ authenticateReset();
 #if USE_WIN32_SERVICE
 
     WIN32_svcstatusupdate(SERVICE_STOP_PENDING, 10000);

=== modified file 'src/peer_userhash.cc'
--- src/peer_userhash.cc 2009-02-24 23:52:44 +0000
+++ src/peer_userhash.cc 2009-12-17 04:48:51 +0000
@@ -180,7 +180,7 @@
     if (n_userhash_peers == 0)
         return NULL;
 
- if (request->auth_user_request)
+ if (request->auth_user_request != NULL)
         key = request->auth_user_request->username();
 
     if (!key)

=== modified file 'src/protos.h'
--- src/protos.h 2010-05-14 12:34:31 +0000
+++ src/protos.h 2010-05-23 12:00:47 +0000
@@ -789,4 +789,8 @@
             /* upstream proxy authentication */
             SQUIDCEXTERN char *peer_proxy_negotiate_auth(char *principal_name, char *proxy);
 #endif
+
+/* call to ensure the auth component schemes exist. */
+SQUIDCEXTERN void InitAuthSchemes(void);
+
 #endif /* SQUID_PROTOS_H */

=== modified file 'src/redirect.cc'
--- src/redirect.cc 2010-04-17 02:29:04 +0000
+++ src/redirect.cc 2010-04-17 10:38:50 +0000
@@ -136,7 +136,7 @@
         r->client_addr.SetNoAddr();
     r->client_ident = NULL;
 
- if (http->request->auth_user_request)
+ if (http->request->auth_user_request != NULL)
         r->client_ident = http->request->auth_user_request->username();
     else if (http->request->extacl_user.defined()) {
         r->client_ident = http->request->extacl_user.termedBuf();

=== modified file 'src/stat.cc'
--- src/stat.cc 2009-12-26 00:25:57 +0000
+++ src/stat.cc 2010-04-17 10:38:50 +0000
@@ -1040,6 +1040,9 @@
     manager->registerAction("active_requests",
                             "Client-side Active Requests",
                             statClientRequests, 0, 1);
+ manager->registerAction("username_cache",
+ "Active Cached Usernames",
+ AuthUser::UsernameCacheStats, 0, 1);
 #if DEBUG_OPENFD
     manager->registerAction("openfd_objects", "Objects with Swapout files open",
                             statOpenfdObj, 0, 0);
@@ -1653,7 +1656,7 @@
                           (int) http->start_time.tv_usec,
                           tvSubDsec(http->start_time, current_time));
 
- if (http->request->auth_user_request)
+ if (http->request->auth_user_request != NULL)
             p = http->request->auth_user_request->username();
         else if (http->request->extacl_user.defined()) {
             p = http->request->extacl_user.termedBuf();

=== modified file 'src/structs.h'
--- src/structs.h 2010-04-17 02:29:04 +0000
+++ src/structs.h 2010-04-17 10:38:50 +0000
@@ -483,7 +483,6 @@
 #endif
     } accessList;
     acl_deny_info_list *denyInfoList;
- authConfig authConfiguration;
 
     struct {
         size_t list_width;

=== modified file 'src/tests/testAuth.cc'
--- src/tests/testAuth.cc 2009-12-26 00:25:57 +0000
+++ src/tests/testAuth.cc 2010-04-24 03:42:16 +0000
@@ -59,22 +59,22 @@
 AuthConfig *
 getConfig(char const *type_str)
 {
- Vector<AuthConfig *> &config = Config.authConfiguration;
+ Auth::authConfig &config = Auth::TheConfig;
     /* find a configuration for the scheme */
- AuthConfig *scheme = AuthConfig::Find (type_str);
+ AuthConfig *scheme = AuthConfig::Find(type_str);
 
     if (scheme == NULL) {
         /* Create a configuration */
- AuthScheme *theScheme;
+ AuthScheme::Pointer theScheme = AuthScheme::Find(type_str);
 
- if ((theScheme = AuthScheme::Find(type_str)) == NULL) {
+ if (theScheme == NULL) {
             return NULL;
             //fatalf("Unknown authentication scheme '%s'.\n", type_str);
         }
 
         config.push_back(theScheme->createConfig());
         scheme = config.back();
- assert (scheme);
+ assert(scheme);
     }
 
     return scheme;
@@ -84,7 +84,7 @@
 void
 setup_scheme(AuthConfig *scheme, char const **params, unsigned param_count)
 {
- Vector<AuthConfig *> &config = Config.authConfiguration;
+ Auth::authConfig &config = Auth::TheConfig;
 
     for (unsigned position=0; position < param_count; position++) {
         char *param_str=xstrdup(params[position]);
@@ -104,7 +104,7 @@
 
     Mem::Init();
 
- Vector<AuthConfig *> &config = Config.authConfiguration;
+ Auth::authConfig &config = Auth::TheConfig;
 
     char const *digest_parms[]= {"program /home/robertc/install/squid/libexec/digest_pw_auth /home/robertc/install/squid/etc/digest.pwd",
                                  "realm foo"
@@ -155,8 +155,8 @@
     Debug::Levels[29]=9;
     fake_auth_setup();
 
- for (AuthScheme::const_iterator i = AuthScheme::Schemes().begin(); i != AuthScheme::Schemes().end(); ++i) {
- AuthUserRequest *authRequest = AuthConfig::CreateAuthUser(find_proxy_auth((*i)->type()));
+ for (AuthScheme::iterator i = AuthScheme::GetSchemes().begin(); i != AuthScheme::GetSchemes().end(); ++i) {
+ AuthUserRequest::Pointer authRequest = AuthConfig::CreateAuthUser(find_proxy_auth((*i)->type()));
         CPPUNIT_ASSERT(authRequest != NULL);
     }
 }
@@ -174,15 +174,16 @@
     Debug::Levels[29]=9;
     fake_auth_setup();
 
- for (AuthScheme::const_iterator i = AuthScheme::Schemes().begin(); i != AuthScheme::Schemes().end(); ++i) {
+ for (AuthScheme::iterator i = AuthScheme::GetSchemes().begin(); i != AuthScheme::GetSchemes().end(); ++i) {
         // create a user request
         // check its scheme matches *i
- AuthUserRequest *authRequest = AuthConfig::CreateAuthUser(find_proxy_auth((*i)->type()));
+ AuthUserRequest::Pointer authRequest = AuthConfig::CreateAuthUser(find_proxy_auth((*i)->type()));
         CPPUNIT_ASSERT_EQUAL(authRequest->scheme(), *i);
     }
 }
 
 #if HAVE_AUTH_MODULE_BASIC
+#include "auth/basic/basicUserRequest.h"
 #include "auth/basic/auth_basic.h"
 /* AuthBasicUserRequest::AuthBasicUserRequest works
  */
@@ -197,14 +198,11 @@
 void
 testAuthBasicUserRequest::username()
 {
- AuthBasicUserRequest();
- AuthBasicUserRequest *temp=new AuthBasicUserRequest();
+ AuthUserRequest::Pointer temp = new AuthBasicUserRequest();
     BasicUser *basic_auth=new BasicUser(AuthConfig::Find("basic"));
     basic_auth->username("John");
     temp->user(basic_auth);
- basic_auth->addRequest(temp);
     CPPUNIT_ASSERT_EQUAL(0, strcmp("John", temp->username()));
- delete temp;
 }
 #endif /* HAVE_AUTH_MODULE_BASIC */
 
@@ -223,14 +221,11 @@
 void
 testAuthDigestUserRequest::username()
 {
- AuthDigestUserRequest();
- AuthDigestUserRequest *temp=new AuthDigestUserRequest();
- DigestUser *user=new DigestUser(AuthConfig::Find("digest"));
- user->username("John");
- temp->user(user);
- user->addRequest(temp);
+ AuthUserRequest::Pointer temp = new AuthDigestUserRequest();
+ DigestUser *duser=new DigestUser(AuthConfig::Find("digest"));
+ duser->username("John");
+ temp->user(duser);
     CPPUNIT_ASSERT_EQUAL(0, strcmp("John", temp->username()));
- delete temp;
 }
 #endif /* HAVE_AUTH_MODULE_DIGEST */
 
@@ -249,14 +244,11 @@
 void
 testAuthNTLMUserRequest::username()
 {
- AuthNTLMUserRequest();
- AuthNTLMUserRequest *temp=new AuthNTLMUserRequest();
- NTLMUser *user=new NTLMUser(AuthConfig::Find("ntlm"));
- user->username("John");
- temp->user(user);
- user->addRequest(temp);
+ AuthUserRequest::Pointer temp = new AuthNTLMUserRequest();
+ NTLMUser *nuser=new NTLMUser(AuthConfig::Find("ntlm"));
+ nuser->username("John");
+ temp->user(nuser);
     CPPUNIT_ASSERT_EQUAL(0, strcmp("John", temp->username()));
- delete temp;
 }
 #endif /* HAVE_AUTH_MODULE_NTLM */
 
@@ -275,14 +267,11 @@
 void
 testAuthNegotiateUserRequest::username()
 {
- AuthNegotiateUserRequest();
- AuthNegotiateUserRequest *temp=new AuthNegotiateUserRequest();
- NegotiateUser *user=new NegotiateUser(AuthConfig::Find("negotiate"));
- user->username("John");
- temp->user(user);
- user->addRequest(temp);
+ AuthUserRequest::Pointer temp = new AuthNegotiateUserRequest();
+ NegotiateUser *nuser=new NegotiateUser(AuthConfig::Find("negotiate"));
+ nuser->username("John");
+ temp->user(nuser);
     CPPUNIT_ASSERT_EQUAL(0, strcmp("John", temp->username()));
- delete temp;
 }
 
 #endif /* HAVE_AUTH_MODULE_NEGOTIATE */

=== modified file 'src/tests/testAuth.h'
--- src/tests/testAuth.h 2009-12-26 00:25:57 +0000
+++ src/tests/testAuth.h 2010-04-17 10:38:50 +0000
@@ -48,6 +48,7 @@
 };
 
 #if HAVE_AUTH_MODULE_BASIC
+#include "auth/basic/basicUserRequest.h"
 class testAuthBasicUserRequest : public CPPUNIT_NS::TestFixture
 {
     CPPUNIT_TEST_SUITE( testAuthBasicUserRequest );
@@ -64,6 +65,7 @@
 #endif
 
 #if HAVE_AUTH_MODULE_DIGEST
+#include "auth/digest/digestUserRequest.h"
 class testAuthDigestUserRequest : public CPPUNIT_NS::TestFixture
 {
     CPPUNIT_TEST_SUITE( testAuthDigestUserRequest );
@@ -80,6 +82,7 @@
 #endif
 
 #if HAVE_AUTH_MODULE_NTLM
+#include "auth/ntlm/ntlmUserRequest.h"
 class testAuthNTLMUserRequest : public CPPUNIT_NS::TestFixture
 {
     CPPUNIT_TEST_SUITE( testAuthNTLMUserRequest );
@@ -96,6 +99,7 @@
 #endif
 
 #if HAVE_AUTH_MODULE_NEGOTIATE
+#include "auth/negotiate/negotiateUserRequest.h"
 class testAuthNegotiateUserRequest : public CPPUNIT_NS::TestFixture
 {
     CPPUNIT_TEST_SUITE( testAuthNegotiateUserRequest );

=== modified file 'src/typedefs.h'
--- src/typedefs.h 2009-11-22 20:37:27 +0000
+++ src/typedefs.h 2010-04-11 09:02:42 +0000
@@ -54,10 +54,6 @@
 /// \deprecated Use AuthUserHashPointer instead.
 typedef struct AuthUserHashPointer auth_user_hash_pointer;
 
-/// \ingroup AuthAPI
-/// \deprecated Use AuthUserIP instead.
-typedef struct AuthUserIP auth_user_ip_t;
-
 /* temporary: once Config is fully hidden, this shouldn't be needed */
 #include "Array.h"
 

=== modified file 'test-suite/Makefile.am'
--- test-suite/Makefile.am 2009-11-09 11:25:11 +0000
+++ test-suite/Makefile.am 2010-01-17 11:58:04 +0000
@@ -67,7 +67,7 @@
 mem_hdr_test_LDADD = $(top_builddir)/src/stmem.o \
                      $(top_builddir)/src/mem_node.o $(LDADD)
 MemPoolTest_SOURCES = MemPoolTest.cc
-refcount_SOURCES = refcount.cc
+refcount_SOURCES = refcount.cc $(DEBUG_SOURCE)
 
 splay_SOURCES = splay.cc
 

# Begin bundle
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
uMLDOD10/PUvvti4vnfh10iOpogph5DEB1V66YfWU/jkvIvXfCU/JiLwn5gRL+qICfBQDIPYaED/
qYVhCpJKo4wsQXEKqEi41TIXgnE/y6q5bm4sfD9pL27z7O+HH2L09n6Gg5XPsOc6D4k1Gk1io3SR
kGECER31FIigAf2EO38/5ef7v5+tf6eG/h/4rfz/8/9H9Lyh/S5GLqurznSUlqJDwFkJusvJW5Vi
3pY4yJ1GXMmZncUp9l4qrqtaY2qytQVvV1tiAQorJeTU5oh4F0nlUksFkwckpCzcvM4hWuaqnEha
cUzeovGJ7MTE4dMBzktGC3GbuZr7jp8kLYxbmxuachGRiwwkpH6n7P638n+z/h/T9x+dPuPX7PT/
q8lfYn00r/Wf9rcNtt+XyxbXv27+HgvLNEnN3jxSmrvWtFhSdFpsbNPDzS3MdSrq6hVDpSTb7ezO
hxo3Xc1UyJuJepjLGlZYwxdq/tf4fw/2M8+Px/kIJ/WIQ/rmh+ihT8hZthMQyygKiolKFGRLFDRL
UBn9yoUcKocBwlHCO9cLtzWFuCJCBdOBgXDBDCkwOF3yi7DMJmClLA5lugXQbrcG6hmAzXHBYBgx
UgzZNyHZ2bJuMOZYdnQGic7sNzfeQwe4WdTmTZOjr+H+siw/a/ZpixiqxVMTMriZhWKgrLSoqplq
RFYxVURRVWIoxQRBFRYioLFBRYIxURYqxEVRRRVFFUVYooCIxH4WiY44MXErMbbCsUVzMhmWiVow
y1FXLjVyqW0RYhjKqZ6nHlsl8mUTBKU7zHBRW10YZhajBMwx/JqmXVQtwwKIBtUSEaCBtMsMoyME
hpK5K4XMzQalEWFY7uaK3VLtMc0Yk2mmCqmWaytUK4rcSlpYyO2sdoYmnTK1d61oTRLWbwybboxp
bozSRy0TUuWmCZcEzKgatBLZNZPzM93t/bceHp0/vM5enTt+L7Pyfk9NdPlr7Ks1wuVwuVdZrBXX
3LFUrRjm3r092Jey1a/yvx9ntRZ/KNbi0j9L4W+J+qO9/fEior3P5THp62n9THWTvj8yZ+et79nP
c8pfD5e/FJ8PceTu9TZzLXkv8Vfzu+uT+mrf6cyOuyNv4in+l2Iz3UzJFRfKT+ZGbWiDV7xKJKE4
OPvaEpS0ria4q8/XStODa87tq82zRYRpLlZaRnCer7eF9t9cTabrw9oZYwrShXSVKyk325lDmLXI
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
wZuYqalSIKKiLPAtbi94yFFxy3Q5H1OqdzgOFXCiT2oVZXYdMJvF3tssyq0fZQiROSo4Q6kNe5M3
AqqCcqhFpKZWzixS+sISFi5OxxrPFMI9UwKKKnW4IijZERERSRgyWMjAhLuQQHC6IO+QBDRq1Zbh
II9jU834DyZswNoo/V4J8zYm9i9lPbm6STmlNXcRAvJXVZiJSltoy1bnZrhpKElKLHt0dNGiNE09
0lJRtxs2c31Sau3xwfIw5OT5HR8TCnY7fn7ztk2zSCK4W34LyUhGOkowFltSHXCGu+N7MKsGsggb
FwH2HklJJJtqpKJNFikSScxD0tEdNGy8Yg9N2iQfHNo2p6fGhbMolKWkWXSaWFKJFyXC1oU2eHC2
sXwSmlE1NF2r78u3aarkwbGTqcGgYIDhksTBbi7EDEOetC/F4symGgT2NBlqtkX6S6llBQQem1PH
XWdnVAepGckQwiKRBsbR4lt2lhCPc4YYR8r1pFYkSjfkv+Gt4u8hdFOdlXt0sxIlyizbtChlguOB
iXLWc5tLHgjuN4x3wUJFjY2myDmTVaMyh+FVn2SiTIsUe6UxKSSSvjVUtEStK2E16PqIRA9t32TS
aHtRw1fZ7ePsrCFXbZVqu0g+z7O1WzZhZp5+HkJwSSBwRhGiEDKKINeg385vmw3y+osO0/BdJ8dP
Hp8XZSfXqrd42j4wl22T7aJMKPpoq3TYdqv8Tnmj/JCy7xujfynWjlJJ2va99HiyiTJI0drqsvGi
TKiz2mkk1d90aG667p09tnbhdd0440bvbZJu2eJOmyzlttu3VaJu0nL4l3nPbpWLdO2ztJRs5OGV
13SrZ23SZTWSVTUXUbvTVu2ekjhqsm0VYd9+mElW7pVRJyy6D+kXxys224vfx6YbvTpwwuiiSizR
0u9NXijdV6XbNiazdqyq4auGwg3UMsMPTVowmy5VLtmqq6uz+2I/gyos4bMtHS7l/FRw/zw0dOnD
hJRu8emXLZq5enSjC6iy7DL4+NXL6ZXVcJsqum6rdVhNlwmzDF5+0uTZa1Gy6rDtoms2TLu3i6Td
oo88sy/kauFGjt6dllE01VFF3Lx0o8drPTkw0XYeJD6g9d6fIQHnFNg7l0C4bu/SG+Bvh+wBDqba
FN93bXnB952i6HsdAp1+sRDSdY0+zES4Gc2WQA6vENKc0TXltwgIe0U6hfADj4tDf3/Y0LQKDd/P
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
7kinChIQv7wU4A==
Received on Sun May 23 2010 - 12:43:41 MDT

This archive was generated by hypermail 2.2.0 : Sun May 23 2010 - 12:00:54 MDT