]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Negotiate authentication scheme support.
authorhno <>
Sun, 23 Oct 2005 17:55:31 +0000 (17:55 +0000)
committerhno <>
Sun, 23 Oct 2005 17:55:31 +0000 (17:55 +0000)
Originally written for Squid-2.5 by Henrik, ported to Squid-3 by Kinkie
and bugfixed by Henrik.

25 files changed:
configure.in
src/ACL.h
src/ACLARP.cc
src/ACLChecklist.cc
src/AuthUser.h
src/AuthUserRequest.cc
src/AuthUserRequest.h
src/Makefile.am
src/acl.cc
src/auth/Makefile.am
src/auth/negotiate/auth_negotiate.cc [new file with mode: 0644]
src/auth/negotiate/auth_negotiate.h [new file with mode: 0644]
src/auth/negotiate/negotiateScheme.cc [new file with mode: 0644]
src/auth/negotiate/negotiateScheme.h [new file with mode: 0644]
src/auth/ntlm/auth_ntlm.cc
src/auth/ntlm/auth_ntlm.h
src/cache_cf.cc
src/cf.data.pre
src/client_side.cc
src/enums.h
src/helper.cc
test-suite/negotiate_test.sh [new file with mode: 0644]
test-suite/ntlm_test.sh [new file with mode: 0644]
test-suite/run_negotiate_test.sh [new file with mode: 0644]
test-suite/run_ntlm_test.sh [new file with mode: 0644]

index 14603031cdfd5bfb780c0f271d36c38e09a82c0d..124d4c6c9c5f2363c62aef2126ab5e24d2ab8380 100644 (file)
@@ -3,7 +3,7 @@ dnl  Configuration input file for Squid
 dnl
 dnl  Duane Wessels, wessels@nlanr.net, February 1996 (autoconf v2.9)
 dnl
-dnl  $Id: configure.in,v 1.387 2005/10/16 19:57:40 serassio Exp $
+dnl  $Id: configure.in,v 1.388 2005/10/23 11:55:31 hno Exp $
 dnl
 dnl
 dnl
@@ -13,7 +13,7 @@ AC_CONFIG_SRCDIR([src/main.cc])
 AC_CONFIG_AUX_DIR(cfgaux)
 AM_INIT_AUTOMAKE(squid, 3.0-PRE3-CVS)
 AM_CONFIG_HEADER(include/autoconf.h)
-AC_REVISION($Revision: 1.387 $)dnl
+AC_REVISION($Revision: 1.388 $)dnl
 AC_PREFIX_DEFAULT(/usr/local/squid)
 AM_MAINTAINER_MODE
 
@@ -1249,7 +1249,7 @@ AC_ARG_ENABLE(auth,
   esac
 ],
 [ if test -z "$AUTH_MODULES"; then
-    AUTH_MODULES="basic digest ntlm"
+    AUTH_MODULES="ntlm basic digest negotiate"
   fi
 ])
 if test -n "$AUTH_MODULES"; then
index 2136d8b44ad4b62f6073e4c345c8f1d3fe10b494..db7e8dc05482080de2357c809ed364ecea268fc7 100644 (file)
--- a/src/ACL.h
+++ b/src/ACL.h
@@ -1,6 +1,6 @@
 
 /*
- * $Id: ACL.h,v 1.13 2005/05/06 01:57:55 hno Exp $
+ * $Id: ACL.h,v 1.14 2005/10/23 11:55:31 hno Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -76,7 +76,6 @@ public:
     virtual bool requiresRequest() const;
     virtual bool requiresReply() const;
     virtual int match(ACLChecklist * checklist) = 0;
-    virtual wordlist *dumpGeneric() const;
     virtual wordlist *dump() const = 0;
     virtual bool empty () const = 0;
     virtual bool valid () const;
index 275331c8351b750fac7d530f7b973cf0e6dda0a7..09ed68c44e8cdb60ca321a9c09719a7805fa5f47 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: ACLARP.cc,v 1.18 2005/05/08 22:35:03 hno Exp $
+ * $Id: ACLARP.cc,v 1.19 2005/10/23 11:55:31 hno Exp $
  *
  * DEBUG: section 28    Access Control
  * AUTHOR: Duane Wessels
@@ -605,7 +605,7 @@ aclMatchArp(SplayNode<acl_arp_data *> **dataptr, struct IN_ADDR c)
 
 #else
 
-    WRITE ME;
+#error "ARP type ACL not supported on this operating system."
 
 #endif
     /*
index b81ed6ce5b6908da4784b4d79a8b546a5a5fd8ee..339b58236f0162ca4eed011b0607ac23a6270df6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: ACLChecklist.cc,v 1.27 2005/10/16 14:52:52 serassio Exp $
+ * $Id: ACLChecklist.cc,v 1.28 2005/10/23 11:55:31 hno Exp $
  *
  * DEBUG: section 28    Access Control
  * AUTHOR: Duane Wessels
@@ -61,7 +61,7 @@ ACLChecklist::authenticated()
     }
 
     /* get authed here */
-    /* Note: this fills in auth_user_request when applicable (auth incomplete)*/
+    /* Note: this fills in auth_user_request when applicable */
     switch (AuthUserRequest::tryToAuthenticateAndSetAuthUser (&auth_user_request, headertype, request, conn(), src_addr)) {
 
     case AUTH_ACL_CANNOT_AUTHENTICATE:
@@ -221,6 +221,18 @@ ACLChecklist::checkCallback(allow_t answer)
     PF *callback_;
     void *cbdata_;
     debug(28, 3) ("ACLChecklist::checkCallback: %p answer=%d\n", this, answer);
+    /* During reconfigure, we can end up not finishing call
+     * sequences into the auth code */
+
+    if (auth_user_request) {
+        /* the checklist lock */
+        auth_user_request->unlock();
+        /* it might have been connection based */
+        assert(conn().getRaw() != NULL);
+        conn()->auth_user_request = NULL;
+        conn()->auth_type = AUTH_BROKEN;
+        auth_user_request = NULL;
+    }
 
     callback_ = callback;
     callback = NULL;
@@ -311,21 +323,6 @@ ACLChecklist::~ACLChecklist()
     if (extacl_entry)
         cbdataReferenceDone(extacl_entry);
 
-    /* During reconfigure or if authentication is used in aclCheckFast without
-     * first being authenticated in http_access we can end up not finishing call
-     * sequences into the auth code. In such case we must make sure to forget
-     * the authentication state completely
-     */
-    if (auth_user_request) {
-        /* the checklist lock */
-        auth_user_request->unlock();
-        /* it might have been connection based */
-        assert(conn().getRaw() != NULL);
-        conn()->auth_user_request = NULL;
-        conn()->auth_type = AUTH_BROKEN;
-        auth_user_request = NULL;
-    }
-
     if (request)
         requestUnlink(request);
 
index f27f4a67ec796b134753aafca95754c94dc9a72b..1c3fe86e6264e589cb3e24505c697253ce317bb6 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: AuthUser.h,v 1.1 2004/08/30 03:28:56 robertc Exp $
+ * $Id: AuthUser.h,v 1.2 2005/10/23 11:55:31 hno Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -62,7 +62,6 @@ public:
      * but how many requests will a single username have in parallel? */
     dlink_list requests;
 
-public:
     static void cacheInit ();
     static void CachedACLsReset();
 
index 4e356786a0469ec91c9dbc2e507b88f80ef399ff..9de00ad4a6b5e9274008b8d0f44778621716b44a 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: AuthUserRequest.cc,v 1.5 2005/10/16 16:47:02 serassio Exp $
+ * $Id: AuthUserRequest.cc,v 1.6 2005/10/23 11:55:31 hno Exp $
  *
  * DO NOT MODIFY NEXT 2 LINES:
  * arch-tag: 6803fde1-d5a2-4c29-9034-1c0c9f650eb4
@@ -449,6 +449,7 @@ AuthUserRequest::authenticate(auth_user_request_t ** auth_user_request, http_hdr
      */
     if (proxy_auth && conn.getRaw() != NULL && conn->auth_user_request &&
             authenticateUserAuthenticated(conn->auth_user_request) &&
+            conn->auth_user_request->connLastHeader() != NULL &&
             strcmp(proxy_auth, conn->auth_user_request->connLastHeader()))
     {
         debug(28, 2) ("authenticateAuthenticate: DUPLICATE AUTH - authentication header on already authenticated connection!. AU %p, Current user '%s' proxy_auth %s\n", conn->auth_user_request, conn->auth_user_request->username(), proxy_auth);
@@ -478,9 +479,9 @@ AuthUserRequest::authenticate(auth_user_request_t ** auth_user_request, http_hdr
                       conn.getRaw() != NULL ? conn->fd : -1);
 
         if (proxy_auth && !request->auth_user_request && conn.getRaw() && conn->auth_user_request) {
-            AuthScheme * id = AuthScheme::Find(proxy_auth);
+            AuthConfig * scheme = AuthConfig::Find(proxy_auth);
 
-            if (!conn->auth_user_request->user() || AuthScheme::Find(conn->auth_user_request->user()->config->type()) != id) {
+            if (!conn->auth_user_request->user() || conn->auth_user_request->user()->config != scheme) {
                 debug(28, 1) ("authenticateAuthenticate: Unexpected change of authentication scheme from '%s' to '%s' (client %s)\n",
                               conn->auth_user_request->user()->config->type(), proxy_auth, inet_ntoa(src_addr));
                 conn->auth_user_request->unlock();
@@ -636,23 +637,9 @@ AuthUserRequest::tryToAuthenticateAndSetAuthUser(auth_user_request_t ** auth_use
     if (t && t->lastReply != AUTH_ACL_CANNOT_AUTHENTICATE
             && t->lastReply != AUTH_ACL_HELPER)
     {
-        if (!*auth_user_request) {
+        if (!*auth_user_request)
             *auth_user_request = t;
 
-            (*auth_user_request)->lock()
-
-            ;
-            //TODO: check if needed. If there's a leak, it is not
-        }
-
-        if (!request->auth_user_request) {
-            request->auth_user_request=t;
-
-            request->auth_user_request->lock()
-
-            ;
-        }
-
         return t->lastReply;
     }
 
@@ -765,16 +752,15 @@ void
 
 AuthUserRequest::lock()
 {
-    debug(29, 9) ("AuthUserRequest::lock: auth_user request '%p'.\n", this);
-    assert(this != NULL);
+    debug(29, 9) ("AuthUserRequest::lock: auth_user request '%p' (%ld references).\n", this, (long int) references);
+    assert(this);
     ++references;
-    debug(29, 9) ("AuthUserRequest::lock: auth_user request '%p' now at '%ld'.\n", this, (long int) references);
 }
 
 void
 AuthUserRequest::unlock()
 {
-    debug(29, 9) ("AuthUserRequest::unlock: auth_user request '%p'.\n", this);
+    debug(29, 9) ("AuthUserRequest::unlock: auth_user request '%p' (%ld references) .\n", this, (long int) references);
     assert(this != NULL);
 
     if (references > 0) {
@@ -783,11 +769,11 @@ AuthUserRequest::unlock()
         debug(29, 1) ("Attempt to lower Auth User request %p refcount below 0!\n", this);
     }
 
-    debug(29, 9) ("AuthUserRequest::unlock: auth_user_request '%p' now at '%ld'.\n", this, (long int) references);
-
-    if (references == 0)
+    if (references == 0) {
+        debug(29, 9) ("AuthUserRequest::unlock: deleting auth_user_request '%p'.\n", this);
         /* not locked anymore */
         delete this;
+    }
 }
 
 AuthScheme *
index f1fffeb7e15300719f9a3877df8e65f9a1d9ad3b..a075def8a902f0622bc381dc118fdad81174d0be 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: AuthUserRequest.h,v 1.3 2005/05/06 01:57:55 hno Exp $
+ * $Id: AuthUserRequest.h,v 1.4 2005/10/23 11:55:31 hno Exp $
  *
  * DO NOT MODIFY NEXT 2 LINES:
  * arch-tag: 674533af-8b21-4641-b71a-74c4639072a0
@@ -70,7 +70,6 @@ public:
     virtual void addHeader(HttpReply * rep, int accel);
     virtual void addTrailer(HttpReply * rep, int accel);
     virtual void onConnectionClose(ConnStateData *);
-    virtual const char *connLastHeader();
     /* template method */
     virtual void module_start(RH *, void *) = 0;
     virtual AuthUser *user() {return _auth_user;}
@@ -79,8 +78,6 @@ public:
 
     virtual void user (AuthUser *aUser) {_auth_user=aUser;}
 
-public:
-
     static auth_acl_t tryToAuthenticateAndSetAuthUser(auth_user_request_t **, http_hdr_type, HttpRequest *, ConnStateData::Pointer, struct IN_ADDR);
     static void addReplyAuthHeader(HttpReply * rep, auth_user_request_t * auth_user_request, HttpRequest * request, int accelerated, int internal);
 
@@ -96,7 +93,6 @@ public:
     void setDenyMessage (char const *);
     char const * getDenyMessage ();
 
-
     size_t refCount() const;
 
     void lock ()
@@ -108,6 +104,8 @@ public:
 
     AuthScheme *scheme() const;
 
+    virtual const char * connLastHeader();
+
 private:
 
     static auth_acl_t authenticate(auth_user_request_t ** auth_user_request, http_hdr_type headertype, HttpRequest * request, ConnStateData::Pointer conn, struct IN_ADDR src_addr);
index 7cfff8eac678742732e0029f8ccf81c228982c3a..23ade4125ede517ce6d613f8bde51561a4cd327f 100644 (file)
@@ -1,7 +1,7 @@
 #
 #  Makefile for the Squid Object Cache server
 #
-#  $Id: Makefile.am,v 1.112 2005/10/16 19:57:40 serassio Exp $
+#  $Id: Makefile.am,v 1.113 2005/10/23 11:55:31 hno Exp $
 #
 #  Uncomment and customize the following to suit your needs:
 #
@@ -229,7 +229,9 @@ all_AUTHMODULES = \
        auth/digest/digestScheme.cc \
        auth/digest/digestScheme.h \
        auth/ntlm/ntlmScheme.cc \
-       auth/ntlm/ntlmScheme.h 
+       auth/ntlm/ntlmScheme.h \
+       auth/negotiate/negotiateScheme.cc \
+       auth/negotiate/negotiateScheme.h 
 
 EXTRA_squid_SOURCES = \
        $(all_FSMODULES) \
index 08904ac62d3235a43b698db44a4db42c1b113168..0d84b36bea67deecb36ccd4ee3d6a627f5a7b0a0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: acl.cc,v 1.315 2005/05/06 01:57:55 hno Exp $
+ * $Id: acl.cc,v 1.316 2005/10/23 11:55:32 hno Exp $
  *
  * DEBUG: section 28    Access Control
  * AUTHOR: Duane Wessels
@@ -56,11 +56,14 @@ ACL *
 ACL::FindByName(const char *name)
 {
     ACL *a;
+    debug(28, 9) ("ACL::FindByName '%s'\n",name);
 
     for (a = Config.aclList; a; a = a->next)
         if (!strcasecmp(a->name, name))
             return a;
 
+    debug(28,9) ("ACL::FindByName found no match\n");
+
     return NULL;
 }
 
@@ -173,20 +176,29 @@ aclGetDenyInfoPage(acl_deny_info_list ** head, const char *name)
     acl_deny_info_list *A = NULL;
     acl_name_list *L = NULL;
 
+    debug(28,9)("aclGetDenyInfoPage: got called for %s\n",name);
+
     A = *head;
 
-    if (NULL == *head)         /* empty list */
+    if (NULL == *head) {               /* empty list */
+        debug(28,9)("aclGetDenyInfoPage: called for an empty list\n");
         return ERR_NONE;
+    }
 
     while (A) {
         L = A->acl_list;
 
-        if (NULL == L)         /* empty list should never happen, but in case */
+        if (NULL == L) {               /* empty list should never happen, but in case */
+            debug(28,3)("aclGetDenyInfoPage: "
+                        "WARNING, unexpected codepath taken\n");
             continue;
+        }
 
         while (L) {
-            if (!strcmp(name, L->name))
+            if (!strcmp(name, L->name)) {
+                debug(28,8)("aclGetDenyInfoPage: match on %s\n",name);
                 return A->err_page_id;
+            }
 
             L = L->next;
         }
@@ -194,6 +206,7 @@ aclGetDenyInfoPage(acl_deny_info_list ** head, const char *name)
         A = A->next;
     }
 
+    debug(28,8)("aclGetDenyInfoPage: no match\n");
     return ERR_NONE;
 }
 
@@ -201,14 +214,19 @@ aclGetDenyInfoPage(acl_deny_info_list ** head, const char *name)
 int
 aclIsProxyAuth(const char *name)
 {
+    debug(28,5)("aclIsProxyAuth: called for %s\n",name);
+
     if (NULL == name)
         return false;
 
     ACL *a;
 
-    if ((a = ACL::FindByName(name)))
+    if ((a = ACL::FindByName(name))) {
+        debug(28,5)("aclIsProxyAuth: returning %d\n",a->isProxyAuth());
         return a->isProxyAuth();
+    }
 
+    debug(28,3)("aclIsProxyAuth: WARNING, called for nonexistent ACL\n");
     return false;
 }
 
@@ -240,7 +258,7 @@ aclParseDenyInfoLine(acl_deny_info_list ** head)
     /* first expect a page name */
 
     if ((t = strtok(NULL, w_space)) == NULL) {
-        debug(28, 0) ("%s line %d: %s\n",
+        debug(28, 0) ("aclParseDenyInfoLine: %s line %d: %s\n",
                       cfg_filename, config_lineno, config_input_line);
         debug(28, 0) ("aclParseDenyInfoLine: missing 'error page' parameter.\n");
         return;
@@ -261,7 +279,7 @@ aclParseDenyInfoLine(acl_deny_info_list ** head)
     }
 
     if (A->acl_list == NULL) {
-        debug(28, 0) ("%s line %d: %s\n",
+        debug(28, 0) ("aclParseDenyInfoLine: %s line %d: %s\n",
                       cfg_filename, config_lineno, config_input_line);
         debug(28, 0) ("aclParseDenyInfoLine: deny_info line contains no ACL's, skipping\n");
         memFree(A, MEM_ACL_DENY_INFO_LIST);
@@ -285,7 +303,7 @@ aclParseAccessLine(acl_access ** head)
     /* first expect either 'allow' or 'deny' */
 
     if ((t = strtok(NULL, w_space)) == NULL) {
-        debug(28, 0) ("%s line %d: %s\n",
+        debug(28, 0) ("aclParseAccessLine: %s line %d: %s\n",
                       cfg_filename, config_lineno, config_input_line);
         debug(28, 0) ("aclParseAccessLine: missing 'allow' or 'deny'.\n");
         return;
@@ -298,7 +316,7 @@ aclParseAccessLine(acl_access ** head)
     else if (!strcmp(t, "deny"))
         A->allow = ACCESS_DENIED;
     else {
-        debug(28, 0) ("%s line %d: %s\n",
+        debug(28, 0) ("aclParseAccessLine: %s line %d: %s\n",
                       cfg_filename, config_lineno, config_input_line);
         debug(28, 0) ("aclParseAccessLine: expecting 'allow' or 'deny', got '%s'.\n", t);
         delete A;
@@ -388,8 +406,9 @@ ACL::matchForCache(ACLChecklist *checklist)
  * checked we check it and cache the result. This function is a template
  * method to support caching of multiple acl types.
  * Note that caching of time based acl's is not
- * wise in long lived caches (i.e. the auth_user proxy match cache.
+ * wise in long lived caches (i.e. the auth_user proxy match cache)
  * RBC
+ * TODO: does a dlink_list perform well enough? Kinkie
  */
 int
 ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
@@ -402,7 +421,7 @@ ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
         auth_match = (acl_proxy_auth_match_cache *)link->data;
 
         if (auth_match->acl_data == this) {
-            debug(28, 4) ("ACL::cacheMatchAcl: cache hit on acl '%p'\n", this);
+            debug(28, 4) ("ACL::cacheMatchAcl: cache hit on acl '%s' (%p)\n", name, this);
             return auth_match->matchrv;
         }
 
@@ -414,6 +433,7 @@ ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
     auth_match->matchrv = matchForCache (checklist);
     auth_match->acl_data = this;
     dlinkAddTail(auth_match, &auth_match->link, cache);
+    debug(28,4)("ACL::cacheMatchAcl: miss for '%s'. Adding result %d\n",name,auth_match->matchrv);
     return auth_match->matchrv;
 }
 
@@ -424,6 +444,8 @@ aclCacheMatchFlush(dlink_list * cache)
     dlink_node *link, *tmplink;
     link = cache->head;
 
+    debug(28,8)("aclCacheMatchFlush called for cache %p\n",cache);
+
     while (link) {
         auth_match = (acl_proxy_auth_match_cache *)link->data;
         tmplink = link;
@@ -448,20 +470,26 @@ ACL::requiresRequest() const
 int
 ACL::checklistMatches(ACLChecklist *checklist)
 {
+    int rv;
+
     if (NULL == checklist->request && requiresRequest()) {
-        debug(28, 1) ("WARNING: '%s' ACL is used but there is no"
-                      " HTTP request -- not matching.\n", name);
+        debug(28, 1) ( "ACL::checklistMatches "
+                       "WARNING: '%s' ACL is used but there is no"
+                       " HTTP request -- not matching.\n", name);
         return 0;
     }
 
     if (NULL == checklist->reply && requiresReply()) {
-        debug(28, 1) ("WARNING: '%s' ACL is used but there is no"
-                      " HTTP reply -- not matching.\n", name);
+        debug(28, 1) ( "ACL::checklistMatches "
+                       "WARNING: '%s' ACL is used but there is no"
+                       " HTTP reply -- not matching.\n", name);
         return 0;
     }
 
-    debug(28, 3) ("aclMatchAcl: checking '%s'\n", cfgline);
-    return match(checklist);
+    debug(28, 3) ("ACL::checklistMatches: checking '%s'\n", name);
+    rv= match(checklist);
+    debug(28,3) ("ACL::ChecklistMatches: result for '%s' is %d\n",name,rv);
+    return rv;
 }
 
 bool
@@ -473,9 +501,11 @@ ACLList::matches (ACLChecklist *checklist) const
                   op ? null_string : "!", _acl->name);
 
     if (_acl->checklistMatches(checklist) != op) {
+        debug(28,4)("ACLList::matches: result is false\n");
         return false;
     }
 
+    debug(28,4)("ACLList::matches: result is true\n");
     return true;
 }
 
@@ -489,6 +519,8 @@ aclDestroyAcls(ACL ** head)
 {
     ACL *next = NULL;
 
+    debug(28,8)("aclDestroyACLs: invoked\n");
+
     for (ACL *a = *head; a; a = next) {
         next = a->next;
         delete a;
@@ -499,7 +531,7 @@ aclDestroyAcls(ACL ** head)
 
 ACL::~ACL()
 {
-    debug(28, 3) ("aclDestroyAcls: '%s'\n", cfgline);
+    debug(28, 3) ("ACL::~ACL: '%s'\n", cfgline);
     safe_free(cfgline);
 }
 
@@ -507,6 +539,7 @@ void
 aclDestroyAclList(acl_list ** head)
 {
     acl_list *l;
+    debug(28,8)("aclDestroyAclList: invoked\n");
 
     for (l = *head; l; l = *head) {
         *head = l->next;
@@ -542,6 +575,8 @@ aclDestroyDenyInfoList(acl_deny_info_list ** list)
     acl_name_list *l = NULL;
     acl_name_list *l_next = NULL;
 
+    debug(28,8)("aclDestroyDenyInfoList: invoked\n");
+
     for (a = *list; a; a = a_next) {
         for (l = a->acl_list; l; l = l_next) {
             l_next = l->next;
@@ -556,13 +591,6 @@ aclDestroyDenyInfoList(acl_deny_info_list ** list)
     *list = NULL;
 }
 
-wordlist *
-ACL::dumpGeneric () const
-{
-    debug(28, 3) ("ACL::dumpGeneric: %s type %s\n", name, typeString());
-    return dump();
-}
-
 /*
  * This function traverses all ACL elements referenced
  * by an access list (presumably 'http_access').   If 
@@ -583,18 +611,25 @@ acl_access::containsPURGE() const
     acl_access const *a = this;
     acl_list *b;
 
+    debug(28,6)("acl_access::containsPURGE: invoked for '%s'\n",cfgline);
+
     for (; a; a = a->next) {
         for (b = a->aclList; b; b = b->next) {
             ACLStrategised<method_t> *tempAcl = dynamic_cast<ACLStrategised<method_t> *>(b->_acl);
 
-            if (!tempAcl)
+            if (!tempAcl) {
+                debug(28,7)("acl_access::containsPURGE: can't create tempAcl\n");
                 continue;
+            }
 
-            if (tempAcl->match(METHOD_PURGE))
+            if (tempAcl->match(METHOD_PURGE)) {
+                debug(28,6)("acl_access::containsPURGE:   returning true\n");
                 return true;
+            }
         }
     }
 
+    debug(28,6)("acl_access::containsPURGE:   returning false\n");
     return false;
 }
 
@@ -630,10 +665,15 @@ void *ACL::Prototype::Initialized;
 bool
 ACL::Prototype::Registered(char const *aType)
 {
+    debug(28,7)("ACL::Prototype::Registered: invoked for type %s\n",aType);
+
     for (iterator i = Registry->begin(); i != Registry->end(); ++i)
-        if (!strcmp (aType, (*i)->typeString))
+        if (!strcmp (aType, (*i)->typeString)) {
+            debug(28,7)("ACL::Prototype::Registered:    yes\n");
             return true;
+        }
 
+    debug(28,7)("ACL::Prototype::Registered:    no\n");
     return false;
 }
 
@@ -661,10 +701,14 @@ ACL::Prototype::~Prototype()
 ACL *
 ACL::Prototype::Factory (char const *typeToClone)
 {
+    debug(28,4)("ACL::Prototype::Factory: cloning an object for type '%s'\n",typeToClone);
+
     for (iterator i = Registry->begin(); i != Registry->end(); ++i)
         if (!strcmp (typeToClone, (*i)->typeString))
             return (*i)->prototype->clone();
 
+    debug(28,4)("ACL::Prototype::Factory: cloning failed, no type '%s' available\n",typeToClone);
+
     return NULL;
 }
 
index f18e10afa83b68dcc68afc29cc1cdabc45a4ff09..1856c242472bc118b87f0be6d1e3bd477079fac4 100644 (file)
@@ -1,6 +1,6 @@
 #  Makefile for authentication modules in the Squid Object Cache server
 #
-#  $Id: Makefile.am,v 1.5 2005/08/20 21:08:38 serassio Exp $
+#  $Id: Makefile.am,v 1.6 2005/10/23 11:55:38 hno Exp $
 #
 AUTOMAKE_OPTIONS = subdir-objects
 AM_CFLAGS = @SQUID_CFLAGS@
@@ -9,12 +9,13 @@ AM_CXXFLAGS = @SQUID_CXXFLAGS@
 ##DIST_SUBDIRS = basic digest ntlm
 ##SUBDIRS              = @AUTH_MODULES@
 
-EXTRA_LIBRARIES        = libbasic.a libdigest.a libntlm.a
+EXTRA_LIBRARIES        = libbasic.a libdigest.a libntlm.a libnegotiate.a
 noinst_LIBRARIES       = @AUTH_LIBS@
 
 libbasic_a_SOURCES     = basic/auth_basic.cc basic/auth_basic.h
 libdigest_a_SOURCES    = digest/auth_digest.cc digest/auth_digest.h
 libntlm_a_SOURCES      = ntlm/auth_ntlm.cc ntlm/auth_ntlm.h
+libnegotiate_a_SOURCES = negotiate/auth_negotiate.cc negotiate/auth_negotiate.h negotiate/negotiateScheme.cc negotiate/negotiateScheme.h
 
 INCLUDES      = -I. -I$(top_builddir)/include -I$(top_srcdir)/include \
        -I$(top_srcdir)/src
diff --git a/src/auth/negotiate/auth_negotiate.cc b/src/auth/negotiate/auth_negotiate.cc
new file mode 100644 (file)
index 0000000..b8e7c34
--- /dev/null
@@ -0,0 +1,822 @@
+
+/*
+ * $Id: auth_negotiate.cc,v 1.1 2005/10/23 11:55:38 hno Exp $
+ *
+ * DEBUG: section 29    Negotiate Authenticator
+ * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+/* The functions in this file handle authentication.
+ * They DO NOT perform access control or auditing.
+ * See acl.c for access control and client_side.c for auditing */
+
+
+#include "squid.h"
+#include "auth_negotiate.h"
+#include "authenticate.h"
+#include "Store.h"
+#include "client_side.h"
+#include "HttpReply.h"
+#include "HttpRequest.h"
+/* TODO remove this include */
+#include "negotiateScheme.h"
+
+static void
+authenticateNegotiateReleaseServer(auth_user_request_t * auth_user_request);
+
+
+static void
+authenticateStateFree(authenticateStateData * r)
+{
+    cbdataFree(r);
+}
+
+/* Negotiate Scheme */
+static HLPSCB authenticateNegotiateHandleReply;
+static AUTHSSTATS authenticateNegotiateStats;
+
+static statefulhelper *negotiateauthenticators = NULL;
+
+CBDATA_TYPE(authenticateStateData);
+
+static int authnegotiate_initialised = 0;
+
+//static MemAllocatorProxy *negotiate_user_hash_pool = NULL;
+
+static auth_negotiate_config negotiateConfig;
+
+static hash_table *proxy_auth_cache = NULL;
+
+/*
+ *
+ * Private Functions
+ *
+ */
+
+/* move to negotiateScheme.cc */
+void
+negotiateScheme::done()
+{
+    /* TODO: this should be a Config call. */
+    debug(29, 2) ("negotiateScheme::done: shutting down Negotiate authentication.\n");
+
+    if (negotiateauthenticators)
+        helperStatefulShutdown(negotiateauthenticators);
+
+    authnegotiate_initialised = 0;
+
+    if (!shutting_down)
+        return;
+
+    if (negotiateauthenticators)
+        helperStatefulFree(negotiateauthenticators);
+
+    negotiateauthenticators = NULL;
+
+    debug(29, 2) ("negotiateScheme::done: Negotiate authentication Shutdown.\n");
+}
+
+/* free any allocated configuration details */
+void
+AuthNegotiateConfig::done()
+{
+    if (authenticate)
+        wordlistDestroy(&authenticate);
+}
+
+void
+AuthNegotiateConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
+{
+    wordlist *list = authenticate;
+    storeAppendPrintf(entry, "%s %s", name, "negotiate");
+
+    while (list != NULL) {
+        storeAppendPrintf(entry, " %s", list->key);
+        list = list->next;
+    }
+
+    storeAppendPrintf(entry, "\n%s negotiate children %d\n",
+                      name, authenticateChildren);
+    storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "negotiate", keep_alive ? "on" : "off");
+
+}
+
+AuthNegotiateConfig::AuthNegotiateConfig() : authenticateChildren(5), keep_alive(1)
+{ }
+
+void
+AuthNegotiateConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
+{
+    if (strcasecmp(param_str, "program") == 0) {
+        if (authenticate)
+            wordlistDestroy(&authenticate);
+
+        parse_wordlist(&authenticate);
+
+        requirePathnameExists("authparam negotiate program", authenticate->key);
+    } else if (strcasecmp(param_str, "children") == 0) {
+        parse_int(&authenticateChildren);
+    } else if (strcasecmp(param_str, "keep_alive") == 0) {
+        parse_onoff(&keep_alive);
+    } else {
+        debug(28, 0) ("AuthNegotiateConfig::parse: unrecognised negotiate auth scheme parameter '%s'\n", param_str);
+    }
+
+    /*
+     * disable client side request pipelining. There is a race with
+     * Negotiate when the client sends a second request on an Negotiate
+     * connection before the authenticate challenge is sent. With
+     * this patch, the client may fail to authenticate, but squid's
+     * state will be preserved.  Caveats: this should be a post-parse
+     * test, but that can wait for the modular parser to be integrated.
+     */
+    if (authenticate)
+        Config.onoff.pipeline_prefetch = 0;
+}
+
+const char *
+AuthNegotiateConfig::type() const
+{
+    return negotiateScheme::GetInstance().type();
+}
+
+/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
+ * config file */
+void
+AuthNegotiateConfig::init(AuthConfig * scheme)
+{
+    static unsigned char negotiate_was_already_initialised = 0;
+
+    if (authenticate) {
+#if PLACEHOLDER
+
+        if (!negotiate_user_hash_pool)
+
+            negotiate_user_hash_pool = new MemAllocatorProxy("Negotiate Header Hash Data", sizeof(struct ProxyAuthCachePointer));
+
+#endif
+
+        authnegotiate_initialised = 1;
+
+        if (negotiateauthenticators == NULL)
+            negotiateauthenticators = helperStatefulCreate("negotiateauthenticator");
+
+        if (!proxy_auth_cache)
+            proxy_auth_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
+
+        assert(proxy_auth_cache);
+
+        negotiateauthenticators->cmdline = authenticate;
+
+        negotiateauthenticators->n_to_start = authenticateChildren;
+
+        negotiateauthenticators->ipc_type = IPC_STREAM;
+
+        helperStatefulOpenServers(negotiateauthenticators);
+
+        if (!negotiate_was_already_initialised) {
+            cachemgrRegister("negotiateauthenticator",
+                             "Negotiate User Authenticator Stats",
+                             authenticateNegotiateStats, 0, 1);
+            negotiate_was_already_initialised++;
+        }
+
+        CBDATA_INIT_TYPE(authenticateStateData);
+    }
+}
+
+bool
+AuthNegotiateConfig::active() const
+{
+    return authnegotiate_initialised == 1;
+}
+
+bool
+AuthNegotiateConfig::configured() const
+{
+    if ((authenticate != NULL) && (authenticateChildren != 0)) {
+        debug(29, 9) ("AuthNegotiateConfig::configured: returning configured\n");
+        return true;
+    }
+
+    debug(29, 9) ("AuthNegotiateConfig::configured: returning unconfigured\n");
+    return false;
+}
+
+/* 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:
+        debug(29, 1) ("AuthNegotiateUserRequest::direction: called before Negotiate Authenticate for request %p!. Report a bug to squid-dev.\n",this);
+        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_FINISHED:
+        return 0; /* do nothing */
+
+    case AUTHENTICATE_STATE_DONE:
+        return 0; /* do nothing */
+
+    case AUTHENTICATE_STATE_INITIAL:
+        debug(29, 1) ("AuthNegotiateUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL\n");
+        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(auth_user_request_t *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request)
+{
+    AuthNegotiateUserRequest *negotiate_request;
+
+    if (!request->flags.proxy_keepalive)
+        return;
+
+    if (!authenticate)
+        return;
+
+    /* New request, no user details */
+    if (auth_user_request == NULL) {
+        debug(29, 9) ("AuthNegotiateConfig::fixHeader: Sending type:%d header: 'NEGOTIATE'\n", type);
+        httpHeaderPutStrf(&rep->header, type, "NEGOTIATE");
+
+        if (!keep_alive) {
+            /* drop the connection */
+            httpHeaderDelByName(&rep->header, "keep-alive");
+            request->flags.proxy_keepalive = 0;
+        }
+    } else {
+        negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request);
+
+        switch (negotiate_request->auth_state) {
+
+        case AUTHENTICATE_STATE_FAILED:
+            /* here it makes sense to drop the connection, as auth is
+             * tied to it, even if MAYBE the client could handle it - Kinkie */
+            httpHeaderDelByName(&rep->header, "keep-alive");
+            request->flags.proxy_keepalive = 0;
+            /* fall through */
+
+        case AUTHENTICATE_STATE_FINISHED:
+            /* Special case: authentication finished OK but disallowed by ACL.
+             * Need to start over to give the client another chance.
+             */
+
+            if (negotiate_request->server_blob) {
+                debug(29, 9) ("authenticateNegotiateFixErrorHeader: Sending type:%d header: 'Negotiate %s'\n", type, negotiate_request->server_blob);
+                httpHeaderPutStrf(&rep->header, type, "Negotiate %s", negotiate_request->server_blob);
+                safe_free(negotiate_request->server_blob);
+            } else {
+                debug(29, 9) ("authenticateNegotiateFixErrorHeader: Connection authenticated\n");
+                httpHeaderPutStrf(&rep->header, type, "Negotiate");
+            }
+
+            break;
+
+        case AUTHENTICATE_STATE_NONE:
+            /* semantic change: do not drop the connection.
+             * 2.5 implementation used to keep it open - Kinkie */
+            debug(29, 9) ("AuthNegotiateConfig::fixHeader: Sending type:%d header: 'NEGOTIATE'\n", type);
+            httpHeaderPutStrf(&rep->header, type, "Negotiate");
+            break;
+
+        case AUTHENTICATE_STATE_IN_PROGRESS:
+            /* we're waiting for a response from the client. Pass it the blob */
+            debug(29, 9) ("AuthNegotiateConfig::fixHeader: Sending type:%d header: 'Negotiate %s'\n", type, negotiate_request->server_blob);
+            httpHeaderPutStrf(&rep->header, type, "Negotiate %s", negotiate_request->server_blob);
+            request->flags.must_keepalive = 1;
+            safe_free(negotiate_request->server_blob);
+            break;
+
+
+        default:
+            debug(29, 0) ("AuthNegotiateConfig::fixHeader: state %d.\n", negotiate_request->auth_state);
+            fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n");
+        }
+    }
+}
+
+NegotiateUser::~NegotiateUser()
+{
+    debug(29, 5) ("NegotiateUser::~NegotiateUser: doing nothing to clearNEGOTIATE scheme data for '%p'\n",this);
+}
+
+static stateful_helper_callback_t
+authenticateNegotiateHandleReply(void *data, void *lastserver, char *reply)
+{
+    authenticateStateData *r = static_cast<authenticateStateData *>(data);
+
+    int valid;
+    stateful_helper_callback_t result = S_HELPER_UNKNOWN;
+    char *blob, *arg = NULL;
+
+    auth_user_request_t *auth_user_request;
+    AuthUser *auth_user;
+    NegotiateUser *negotiate_user;
+    AuthNegotiateUserRequest *negotiate_request;
+
+    debug(29, 8) ("authenticateNegotiateHandleReply: helper: '%p' sent us '%s'\n", lastserver, reply ? reply : "<NULL>");
+    valid = cbdataReferenceValid(data);
+
+    if (!valid) {
+        debug(29, 1) ("authenticateNegotiateHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver);
+        cbdataReferenceDone(r->data);
+        authenticateStateFree(r);
+        debug(29, 9) ("authenticateNegotiateHandleReply: telling stateful helper : %d\n", S_HELPER_RELEASE);
+        return S_HELPER_RELEASE;
+    }
+
+    if (!reply) {
+        /*
+         * TODO: this occurs when a helper crashes. We should clean
+         * up that helpers resources and queued requests.
+         */
+        fatal("authenticateNegotiateHandleReply: called with no result string\n");
+    }
+
+    auth_user_request = r->auth_user_request;
+    assert(auth_user_request != NULL);
+    negotiate_request = dynamic_cast<AuthNegotiateUserRequest *>(auth_user_request);
+
+    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());
+
+    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, ' ');
+
+    while (xisspace(*blob)) {    // trim leading spaces in blob
+        blob++;
+        arg = strchr(blob + 1, ' ');
+    }
+
+    if (strncasecmp(reply, "TT ", 3) == 0 && blob != NULL) {
+        /* we have been given a blob to send to the client */
+
+        if (arg)
+            *arg++ = '\0';
+
+        safe_free(negotiate_request->server_blob);
+
+        negotiate_request->server_blob = xstrdup(blob);
+
+        negotiate_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
+
+        auth_user_request->denyMessage("Authenication in progress");
+
+        debug(29, 4) ("authenticateNegotiateHandleReply: Need to challenge the client with a server blob '%s'\n", blob);
+
+        result = S_HELPER_RESERVE;
+    } else if (strncasecmp(reply, "AF ", 3) == 0 && blob != 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);
+
+        authenticateNegotiateReleaseServer(negotiate_request);
+
+        negotiate_request->auth_state = AUTHENTICATE_STATE_FINISHED;
+
+        result = S_HELPER_RELEASE;
+
+        debug(29, 4) ("authenticateNegotiateHandleReply: Successfully validated user via NEGOTIATE. Username '%s'\n", blob);
+    } else if (strncasecmp(reply, "NA ", 3) == 0 && blob != 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);
+
+        authenticateNegotiateReleaseServer(negotiate_request);
+
+        result = S_HELPER_RELEASE;
+
+        debug(29, 4) ("authenticateNegotiateHandleReply: Failed validating user via NEGOTIATE. Error returned '%s'\n", 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);
+        authenticateNegotiateReleaseServer(negotiate_request);
+        result = S_HELPER_RELEASE;
+        debug(29, 1) ("authenticateNegotiateHandleReply: Error validating user via NEGOTIATE. Error returned '%s'\n", reply);
+    } else {
+        /* protocol error */
+        fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
+    }
+
+    r->handler(r->data, NULL);
+    cbdataReferenceDone(r->data);
+    authenticateStateFree(r);
+    debug(29, 9) ("authenticateNegotiateHandleReply: telling stateful helper : %d\n", result);
+    return result;
+}
+
+static void
+authenticateNegotiateStats(StoreEntry * sentry)
+{
+    storeAppendPrintf(sentry, "NEGOTIATE Authenticator Statistics:\n");
+    helperStatefulStats(sentry, negotiateauthenticators);
+}
+
+
+/* send the initial data to a stateful negotiate authenticator module */
+void
+AuthNegotiateUserRequest::module_start(RH * handler, void *data)
+{
+    authenticateStateData *r = NULL;
+    static char buf[8192];
+    negotiate_user_t *negotiate_user;
+    auth_user_t *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());
+
+    debug(29, 8) ("AuthNegotiateUserRequest::module_start: auth state is '%d'\n", auth_state);
+
+    if (negotiateConfig.authenticate == NULL) {
+        debug(29, 0) ("AuthNegotiateUserRequest::module_start: no NEGOTIATE program specified.");
+        handler(data, NULL);
+        return;
+    }
+
+    r = cbdataAlloc(authenticateStateData);
+    r->handler = handler;
+    cbdataReference(data);
+    r->data = data;
+    r->auth_user_request = this;
+
+    lock()
+
+        ;
+    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(negotiateauthenticators, buf, authenticateNegotiateHandleReply, r, authserver);
+}
+
+/* clear the NEGOTIATE helper of being reserved for future requests */
+static void
+authenticateNegotiateReleaseServer(auth_user_request_t * auth_user_request)
+{
+    AuthNegotiateUserRequest *negotiate_request;
+    assert(auth_user_request->user()->auth_type == AUTH_NEGOTIATE);
+    negotiate_request = dynamic_cast< AuthNegotiateUserRequest *>(auth_user_request);
+    debug(29, 9) ("authenticateNegotiateReleaseServer: releasing server '%p'\n", negotiate_request->authserver);
+    /* is it possible for the server to be NULL? hno seems to think so.
+     * Let's see what happens, might segfault in helperStatefulReleaseServer
+     * if it does. I leave it like this not to cover possibly problematic
+     * code-paths. Kinkie */
+    helperStatefulReleaseServer(negotiate_request->authserver);
+    negotiate_request->authserver = NULL;
+}
+
+/* clear any connection related authentication details */
+void
+AuthNegotiateUserRequest::onConnectionClose(ConnStateData *connection)
+{
+    assert(connection != NULL);
+
+    debug(29,8)("AuthNegotiateUserRequest::onConnectionClose: closing connection '%p' (this is '%p')\n",connection,this);
+
+    if (connection->auth_user_request == NULL) {
+        debug(29,8)("AuthNegotiateUserRequest::onConnectionClose: no auth_user_request\n");
+        return;
+    }
+
+    if (authserver != NULL)
+        authenticateNegotiateReleaseServer(this);
+
+    /* unlock the connection based lock */
+    debug(29, 9) ("AuthNegotiateUserRequest::onConnectionClose: Unlocking auth_user from the connection '%p'.\n",connection);
+
+    /* This still breaks the abstraction, but is at least read only now.
+    * If needed, this could be ignored, as the conn deletion will also unlock
+    * the auth user request.
+    */
+    unlock();
+
+    connection->auth_user_request = NULL;
+}
+
+/*
+ * Decode a NEGOTIATE [Proxy-]Auth string, placing the results in the passed
+ * Auth_user structure.
+ */
+AuthUserRequest *
+AuthNegotiateConfig::decode(char const *proxy_auth)
+{
+    NegotiateUser *newUser = new NegotiateUser(&negotiateConfig);
+    AuthNegotiateUserRequest *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 */
+    debug(29, 9) ("AuthNegotiateConfig::decode: NEGOTIATE authentication\n");
+    return auth_user_request;
+}
+
+int
+AuthNegotiateUserRequest::authenticated() const
+{
+    if (auth_state == AUTHENTICATE_STATE_FINISHED) {
+        debug(29, 9) ("AuthNegotiateUserRequest::authenticated: user authenticated.\n");
+        return 1;
+    }
+
+    debug(29, 9) ("AuthNegotiateUserRequest::authenticated: user not fully authenticated.\n");
+
+    return 0;
+}
+
+void
+AuthNegotiateUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer conn, http_hdr_type type)
+{
+    const char *proxy_auth, *blob;
+
+    //ProxyAuthCachePointer *proxy_auth_hash = NULL;
+    auth_user_hash_pointer *usernamehash;
+
+    /* TODO: rename this!! */
+    auth_user_t *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.getRaw() == NULL) {
+        auth_state = AUTHENTICATE_STATE_FAILED;
+        debug(29, 1) ("AuthNegotiateUserRequest::authenticate: attempt to perform authentication without a connection!\n");
+        return;
+    }
+
+    if (waiting) {
+        debug(29, 1) ("AuthNegotiateUserRequest::authenticate: waiting for helper reply!\n");
+        return;
+    }
+
+    if (server_blob) {
+        debug(29,2)("AuthNegotiateUserRequest::authenticate: need to challenge client '%s'!\n", server_blob);
+        return;
+    }
+
+    /* get header */
+    proxy_auth = httpHeaderGetStr(&request->header, type);
+
+    blob = proxy_auth + strlen("Negotiate");
+
+    while (xisspace(*blob))     // trim leading spaces in blob
+        blob++;
+
+    switch (auth_state) {
+
+    case AUTHENTICATE_STATE_NONE:
+        /* we've recieved a negotiate request. pass to a helper */
+        debug(29, 9) ("AuthNegotiateUserRequest::authenticate: auth state negotiate none. Received blob: '%s'\n", proxy_auth);
+        auth_state = AUTHENTICATE_STATE_INITIAL;
+        safe_free(client_blob);
+        client_blob=xstrdup(blob);
+        conn->auth_type = AUTH_NEGOTIATE;
+        conn->auth_user_request = this;
+        conn = conn;
+
+        lock()
+
+            ;
+        return;
+
+        break;
+
+    case AUTHENTICATE_STATE_INITIAL:
+        debug(29,1)("AuthNegotiateUserRequest::authenticate: need to ask helper\n");
+
+        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);
+
+        return;
+
+        break;
+
+    case AUTHENTICATE_STATE_FINISHED:
+        /* connection is authenticated */
+        debug(29, 4) ("AuthNegotiateUserRequest::authenticate: authenticated user %s\n", negotiate_user->username());
+
+        /* see if this is an existing user with a different proxy_auth
+         * string */
+        usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, negotiate_user->username()));
+
+        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();
+            _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;
+
+        authenticateNegotiateReleaseServer(this);
+
+        auth_state = AUTHENTICATE_STATE_DONE;
+
+        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 */
+        debug(29, 9) ("AuthNegotiateUserRequest::authenticate: auth state negotiate failed. %s\n", 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;
+}
+
+AuthNegotiateUserRequest::~AuthNegotiateUserRequest()
+{
+    safe_free(server_blob);
+    safe_free(client_blob);
+
+    if (authserver != NULL) {
+        debug(29, 9) ("AuthNegotiateUserRequest::~AuthNegotiateUserRequest: releasing server '%p'\n", authserver);
+        helperStatefulReleaseServer(authserver);
+        authserver = NULL;
+    }
+}
+
+void
+NegotiateUser::deleteSelf() const
+{
+    delete this;
+}
+
+NegotiateUser::NegotiateUser (AuthConfig *config) : AuthUser (config)
+{
+    proxy_auth_list.head = proxy_auth_list.tail = NULL;
+}
+
+AuthConfig *
+negotiateScheme::createConfig()
+{
+    return &negotiateConfig;
+}
+
+const char *
+AuthNegotiateUserRequest::connLastHeader()
+{
+    return NULL;
+}
+
diff --git a/src/auth/negotiate/auth_negotiate.h b/src/auth/negotiate/auth_negotiate.h
new file mode 100644 (file)
index 0000000..34165b8
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * auth_negotiate.h
+ * Internal declarations for the negotiate auth module
+ */
+
+#ifndef __AUTH_NEGOTIATE_H__
+#define __AUTH_NEGOTIATE_H__
+#include "authenticate.h"
+#include "AuthUser.h"
+#include "AuthUserRequest.h"
+#include "AuthConfig.h"
+
+#define DefaultAuthenticateChildrenMax  32     /* 32 processes */
+
+typedef enum {
+    AUTHENTICATE_STATE_NONE,
+    AUTHENTICATE_STATE_INITIAL,
+    AUTHENTICATE_STATE_IN_PROGRESS,
+    AUTHENTICATE_STATE_FINISHED,
+    AUTHENTICATE_STATE_DONE,
+    AUTHENTICATE_STATE_FAILED
+} auth_state_t;                 /* connection level auth state */
+
+/* Generic */
+
+typedef struct
+{
+    void *data;
+    auth_user_request_t *auth_user_request;
+    RH *handler;
+}
+
+authenticateStateData;
+
+class NegotiateUser : public AuthUser
+{
+
+public:
+    MEMPROXY_CLASS(NegotiateUser);
+    virtual void deleteSelf() const;
+    NegotiateUser(AuthConfig *);
+    ~NegotiateUser();
+    dlink_list proxy_auth_list;
+};
+
+MEMPROXY_CLASS_INLINE(NegotiateUser)
+
+typedef class NegotiateUser negotiate_user_t;
+
+class AuthNegotiateUserRequest : public AuthUserRequest
+{
+
+public:
+    MEMPROXY_CLASS(AuthNegotiateUserRequest);
+
+    AuthNegotiateUserRequest();
+    virtual ~AuthNegotiateUserRequest();
+    virtual int authenticated() const;
+    virtual void authenticate(HttpRequest * request, ConnStateData::Pointer 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;
+    /* what connection is this associated with */
+    ConnStateData::Pointer 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;
+
+private:
+    /* the user */
+    NegotiateUser * _theUser;
+};
+
+MEMPROXY_CLASS_INLINE(AuthNegotiateUserRequest)
+
+/* configuration runtime data */
+
+class AuthNegotiateConfig : public AuthConfig
+{
+
+public:
+    AuthNegotiateConfig::AuthNegotiateConfig();
+    virtual bool active() const;
+    virtual bool configured() const;
+    virtual AuthUserRequest *decode(char const *proxy_auth);
+    virtual void done();
+    virtual void dump(StoreEntry *, const char *, AuthConfig *);
+    virtual void fixHeader(auth_user_request_t *, HttpReply *, http_hdr_type, HttpRequest *);
+    virtual void init(AuthConfig *);
+    virtual void parse(AuthConfig *, int, char *);
+    virtual const char * type() const;
+    int authenticateChildren;
+    int keep_alive;
+    wordlist *authenticate;
+};
+
+typedef class AuthNegotiateConfig auth_negotiate_config;
+
+#endif
diff --git a/src/auth/negotiate/negotiateScheme.cc b/src/auth/negotiate/negotiateScheme.cc
new file mode 100644 (file)
index 0000000..5332465
--- /dev/null
@@ -0,0 +1,53 @@
+
+/*
+ * $Id: negotiateScheme.cc,v 1.1 2005/10/23 11:55:38 hno Exp $
+ *
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#include "negotiateScheme.h"
+
+AuthScheme &
+negotiateScheme::GetInstance()
+{
+    return _instance;
+}
+
+negotiateScheme::negotiateScheme()
+{
+    AddScheme(*this);
+}
+
+char const *
+negotiateScheme::type () const
+{
+    return "negotiate";
+}
+
+negotiateScheme negotiateScheme::_instance;
diff --git a/src/auth/negotiate/negotiateScheme.h b/src/auth/negotiate/negotiateScheme.h
new file mode 100644 (file)
index 0000000..644a296
--- /dev/null
@@ -0,0 +1,59 @@
+
+/*
+ * $Id: negotiateScheme.h,v 1.1 2005/10/23 11:55:38 hno Exp $
+ *
+ *
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
+ * ----------------------------------------------------------
+ *
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ *
+ */
+
+#ifndef SQUID_NEGOTIATESCHEME_H
+#define SQUID_NEGOTIATESCHEME_H
+
+#include "AuthScheme.h"
+
+class negotiateScheme : public AuthScheme
+{
+
+public:
+    static AuthScheme &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;
+};
+
+#endif /* SQUID_negotiateSCHEME_H */
index 5176364756ded779dc264ec71f825f5234eaffc8..1f97db93b46948134129af673e1d7ea2aa8c164b 100644 (file)
@@ -1,9 +1,9 @@
 
 /*
- * $Id: auth_ntlm.cc,v 1.49 2005/10/16 16:47:02 serassio Exp $
+ * $Id: auth_ntlm.cc,v 1.50 2005/10/23 11:55:38 hno Exp $
  *
  * DEBUG: section 29    NTLM Authenticator
- * AUTHOR: Robert Collins
+ * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
  * ----------------------------------------------------------
 /* TODO remove this include */
 #include "ntlmScheme.h"
 
+static void
+authenticateNTLMReleaseServer(auth_user_request_t * auth_user_request);
+
+
 static void
 authenticateStateFree(authenticateStateData * r)
 {
-    r->auth_user_request->unlock();
-    r->auth_user_request = NULL;
     cbdataFree(r);
 }
 
 /* NTLM Scheme */
 static HLPSCB authenticateNTLMHandleReply;
-static HLPSCB authenticateNTLMHandleplaceholder;
 static AUTHSSTATS authenticateNTLMStats;
 
-/* helper callbacks to handle per server state data */
-static HLPSAVAIL authenticateNTLMHelperServerAvailable;
-static HLPSONEQ authenticateNTLMHelperServerOnEmpty;
-
 static statefulhelper *ntlmauthenticators = NULL;
 
 CBDATA_TYPE(authenticateStateData);
 
 static int authntlm_initialised = 0;
 
-static MemAllocatorProxy *ntlm_helper_state_pool = NULL;
-static MemAllocatorProxy *ntlm_user_hash_pool = NULL;
+//static MemAllocatorProxy *ntlm_user_hash_pool = NULL;
 
 static auth_ntlm_config ntlmConfig;
 
@@ -89,7 +85,7 @@ void
 ntlmScheme::done()
 {
     /* TODO: this should be a Config call. */
-    debug(29, 2) ("authNTLMDone: shutting down NTLM authentication.\n");
+    debug(29, 2) ("ntlmScheme::done: shutting down NTLM authentication.\n");
 
     if (ntlmauthenticators)
         helperStatefulShutdown(ntlmauthenticators);
@@ -104,21 +100,7 @@ ntlmScheme::done()
 
     ntlmauthenticators = NULL;
 
-#if DEBUGSHUTDOWN
-
-    if (ntlm_helper_state_pool) {
-        delete ntlm_helper_state_pool;
-        ntlm_helper_state_pool = NULL;
-    }
-
-    /* Removed for some reason..
-        if (ntlm_user_pool) {
-       delete ntlm_user_pool;ntlm_user_pool = NULL;
-        }
-        */
-
-#endif
-    debug(29, 2) ("authNTLMDone: NTLM authentication Shutdown.\n");
+    debug(29, 2) ("ntlmScheme::done: NTLM authentication Shutdown.\n");
 }
 
 /* free any allocated configuration details */
@@ -140,20 +122,14 @@ AuthNTLMConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
         list = list->next;
     }
 
-    storeAppendPrintf(entry, "\n%s %s children %d\n%s %s max_challenge_reuses %d\n%s %s max_challenge_lifetime %d seconds\n",
-                      name, "ntlm", authenticateChildren,
-                      name, "ntlm", challengeuses,
-                      name, "ntlm", (int) challengelifetime);
+    storeAppendPrintf(entry, "\n%s ntlm children %d\n",
+                      name, authenticateChildren);
+    storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "ntlm", keep_alive ? "on" : "off");
 
 }
 
-AuthNTLMConfig::AuthNTLMConfig()
-{
-    /* TODO Move into initialisation list */
-    authenticateChildren = 5;
-    challengeuses = 0;
-    challengelifetime = 60;
-}
+AuthNTLMConfig::AuthNTLMConfig() : authenticateChildren(5), keep_alive(1)
+{ }
 
 void
 AuthNTLMConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
@@ -167,12 +143,10 @@ AuthNTLMConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
         requirePathnameExists("authparam ntlm program", authenticate->key);
     } else if (strcasecmp(param_str, "children") == 0) {
         parse_int(&authenticateChildren);
-    } else if (strcasecmp(param_str, "max_challenge_reuses") == 0) {
-        parse_int(&challengeuses);
-    } else if (strcasecmp(param_str, "max_challenge_lifetime") == 0) {
-        parse_time_t(&challengelifetime);
+    } else if (strcasecmp(param_str, "keep_alive") == 0) {
+        parse_onoff(&keep_alive);
     } else {
-        debug(28, 0) ("unrecognised ntlm auth scheme parameter '%s'\n", param_str);
+        debug(28, 0) ("AuthNTLMConfig::parse: unrecognised ntlm auth scheme parameter '%s'\n", param_str);
     }
 
     /*
@@ -198,16 +172,17 @@ AuthNTLMConfig::type() const
 void
 AuthNTLMConfig::init(AuthConfig * scheme)
 {
-    static int ntlminit = 0;
+    static unsigned char ntlm_was_already_initialised = 0;
 
     if (authenticate) {
-        if (!ntlm_helper_state_pool)
-            ntlm_helper_state_pool = new MemAllocatorProxy("NTLM Helper State data", sizeof(ntlm_helper_state_t));
+#if PLACEHOLDER
 
         if (!ntlm_user_hash_pool)
 
             ntlm_user_hash_pool = new MemAllocatorProxy("NTLM Header Hash Data", sizeof(struct ProxyAuthCachePointer));
 
+#endif
+
         authntlm_initialised = 1;
 
         if (ntlmauthenticators == NULL)
@@ -224,27 +199,13 @@ AuthNTLMConfig::init(AuthConfig * scheme)
 
         ntlmauthenticators->ipc_type = IPC_STREAM;
 
-        ntlmauthenticators->datapool = ntlm_helper_state_pool;
-
-        ntlmauthenticators->IsAvailable = authenticateNTLMHelperServerAvailable;
-
-        ntlmauthenticators->OnEmptyQueue = authenticateNTLMHelperServerOnEmpty;
-
         helperStatefulOpenServers(ntlmauthenticators);
 
-        /*
-         * TODO: In here send the initial YR to preinitialise the
-         * challenge cache
-         */
-        /*
-         * Think about this... currently we ask when the challenge
-         * is needed. Better?
-         */
-        if (!ntlminit) {
+        if (!ntlm_was_already_initialised) {
             cachemgrRegister("ntlmauthenticator",
                              "NTLM User Authenticator Stats",
                              authenticateNTLMStats, 0, 1);
-            ntlminit++;
+            ntlm_was_already_initialised++;
         }
 
         CBDATA_INIT_TYPE(authenticateStateData);
@@ -260,60 +221,55 @@ AuthNTLMConfig::active() const
 bool
 AuthNTLMConfig::configured() const
 {
-    if ((authenticate != NULL) && (authenticateChildren != 0) && (challengeuses > -1) && (challengelifetime > -1)) {
-        debug(29, 9) ("authNTLMConfigured: returning configured\n");
+    if ((authenticate != NULL) && (authenticateChildren != 0)) {
+        debug(29, 9) ("AuthNTLMConfig::configured: returning configured\n");
         return true;
     }
 
-    debug(29, 9) ("authNTLMConfigured: returning unconfigured\n");
+    debug(29, 9) ("AuthNTLMConfig::configured: returning unconfigured\n");
     return false;
 }
 
 /* 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:
-        debug(29, 1) ("AuthNTLMUserRequest::direction: called before NTLM Authenticate!. Report a bug to squid-dev.\n");
-        /* fall thru */
+        debug(29, 1) ("AuthNTLMUserRequest::direction: called before NTLM Authenticate for request %p!. Report a bug to squid-dev.\n",this);
+        return -2; /* error */
 
     case AUTHENTICATE_STATE_FAILED:
-        return -2;
+        return -2; /* error */
 
-        /* send to helper */
 
-    case AUTHENTICATE_STATE_NEGOTIATE:
+    case AUTHENTICATE_STATE_IN_PROGRESS:
+        assert(server_blob);
+        return 1; /* send to client */
 
-        /*send to helper */
-
-    case AUTHENTICATE_STATE_RESPONSE:
-        return -1;
-
-        /* send to client */
-
-    case AUTHENTICATE_STATE_CHALLENGE:
-        return 1;
-
-        /* do nothing.. */
+    case AUTHENTICATE_STATE_FINISHED:
+        return 0; /* do nothing */
 
     case AUTHENTICATE_STATE_DONE:
-        return 0;
+        return 0; /* do nothing */
+
+    case AUTHENTICATE_STATE_INITIAL:
+        debug(29, 1) ("AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL\n");
+        return -2;
     }
 
     return -2;
 }
 
-/*
- * Send the authenticate error header(s). Note: IE has a bug and the NTLM header
- * must be first. To ensure that, the configure use --enable-auth=ntlm, anything
- * else.
- */
 void
 AuthNTLMConfig::fixHeader(auth_user_request_t *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request)
 {
@@ -322,118 +278,87 @@ AuthNTLMConfig::fixHeader(auth_user_request_t *auth_user_request, HttpReply *rep
     if (!request->flags.proxy_keepalive)
         return;
 
-    if (authenticate) {
-        /* New request, no user details */
+    if (!authenticate)
+        return;
 
-        if (auth_user_request == NULL) {
-            debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
-            httpHeaderPutStrf(&rep->header, type, "NTLM");
+    /* New request, no user details */
+    if (auth_user_request == NULL) {
+        debug(29, 9) ("AuthNTLMConfig::fixHeader: Sending type:%d header: 'NTLM'\n", type);
+        httpHeaderPutStrf(&rep->header, type, "NTLM");
+
+        if (!keep_alive) {
             /* drop the connection */
             httpHeaderDelByName(&rep->header, "keep-alive");
-            /* NTLM has problems if the initial connection is not dropped
-             * I haven't checked the RFC compliance of this hack - RBCollins */
             request->flags.proxy_keepalive = 0;
-        } else {
-            ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
-            assert (ntlm_request);
-
-            switch (ntlm_request->auth_state) {
-
-            case AUTHENTICATE_STATE_NONE:
-
-            case AUTHENTICATE_STATE_FAILED:
-                debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
-                httpHeaderPutStrf(&rep->header, type, "NTLM");
-                /* drop the connection */
-                httpHeaderDelByName(&rep->header, "keep-alive");
-                /* NTLM has problems if the initial connection is not dropped
-                 * I haven't checked the RFC compliance of this hack - RBCollins */
-                request->flags.proxy_keepalive = 0;
-                break;
-
-            case AUTHENTICATE_STATE_CHALLENGE:
-                /* we are 'waiting' for a response */
-                /* pass the challenge to the client */
-                debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM %s'\n", type, ntlm_request->authchallenge);
-                httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->authchallenge);
-                request->flags.must_keepalive = 1;
-                break;
-
-            default:
-                debug(29, 0) ("authenticateNTLMFixErrorHeader: state %d.\n", ntlm_request->auth_state);
-                fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
-            }
         }
-    }
-}
+    } else {
+        ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
 
-NTLMUser::~NTLMUser()
-{
-    dlink_node *link, *tmplink;
-    ProxyAuthCachePointer *proxy_auth_hash;
-    debug(29, 5) ("NTLMUser::~NTLMUser: Clearing NTLM scheme data\n");
-
-    /* were they linked in by one or more proxy-authenticate headers */
-    link = proxy_auth_list.head;
-
-    while (link) {
-        debug(29, 9) ("authenticateFreeProxyAuthUser: removing proxy_auth hash entry '%p'\n", link->data);
-        proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(link->data);
-        tmplink = link;
-        link = link->next;
-        dlinkDelete(tmplink, &proxy_auth_list);
-        hash_remove_link(proxy_auth_cache, (hash_link *) proxy_auth_hash);
-        /* free the key (usually the proxy_auth header) */
-        xfree(proxy_auth_hash->key);
-        ntlm_user_hash_pool->free(proxy_auth_hash);
-    }
+        switch (ntlm_request->auth_state) {
 
-}
+        case AUTHENTICATE_STATE_FAILED:
+            /* here it makes sense to drop the connection, as auth is
+             * tied to it, even if MAYBE the client could handle it - Kinkie */
+            httpHeaderDelByName(&rep->header, "keep-alive");
+            request->flags.proxy_keepalive = 0;
+            /* fall through */
 
-static stateful_helper_callback_t
-authenticateNTLMHandleplaceholder(void *data, void *lastserver, char *reply)
-{
-    authenticateStateData *r = static_cast<authenticateStateData *>(data);
-    stateful_helper_callback_t result = S_HELPER_UNKNOWN;
-    /* we should only be called for placeholder requests - which have no reply string */
-    assert(reply == NULL);
-    assert(r->auth_user_request);
-    /* standard callback stuff */
-
-    if (!cbdataReferenceValid(r->data)) {
-        debug(29, 1) ("AuthenticateNTLMHandlePlacheholder: invalid callback data.\n");
-        return result;
-    }
+        case AUTHENTICATE_STATE_FINISHED:
+            /* Special case: authentication finished OK but disallowed by ACL.
+             * Need to start over to give the client another chance.
+             */
+            /* fall through */
 
-    /* call authenticateNTLMStart to retry this request */
-    debug(29, 9) ("authenticateNTLMHandleplaceholder: calling authenticateNTLMStart\n");
+        case AUTHENTICATE_STATE_NONE:
+            /* semantic change: do not drop the connection.
+             * 2.5 implementation used to keep it open - Kinkie */
+            debug(29, 9) ("AuthNTLMConfig::fixHeader: Sending type:%d header: 'NTLM'\n", type);
+            httpHeaderPutStrf(&rep->header, type, "NTLM");
+            break;
 
-    r->auth_user_request->start(r->handler, r->data);
+        case AUTHENTICATE_STATE_IN_PROGRESS:
+            /* we're waiting for a response from the client. Pass it the blob */
+            debug(29, 9) ("AuthNTLMConfig::fixHeader: Sending type:%d header: 'NTLM %s'\n", type, ntlm_request->server_blob);
+            httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->server_blob);
+            request->flags.must_keepalive = 1;
+            safe_free(ntlm_request->server_blob);
+            break;
 
-    cbdataReferenceDone(r->data);
 
-    authenticateStateFree(r);
+        default:
+            debug(29, 0) ("AuthNTLMConfig::fixHeader: state %d.\n", ntlm_request->auth_state);
+            fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
+        }
+    }
+}
 
-    return result;
+NTLMUser::~NTLMUser()
+{
+    debug(29, 5) ("NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '%p'\n",this);
 }
 
 static stateful_helper_callback_t
 authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
 {
     authenticateStateData *r = static_cast<authenticateStateData *>(data);
-    ntlm_helper_state_t *helperstate;
+
+    int valid;
     stateful_helper_callback_t result = S_HELPER_UNKNOWN;
+    char *blob;
+
     auth_user_request_t *auth_user_request;
-    auth_user_t *auth_user;
-    ntlm_user_t *ntlm_user;
+    AuthUser *auth_user;
+    NTLMUser *ntlm_user;
     AuthNTLMUserRequest *ntlm_request;
-    debug(29, 9) ("authenticateNTLMHandleReply: Helper: '%p' {%s}\n", lastserver, reply ? reply : "<NULL>");
 
-    if (!cbdataReferenceValid(r->data)) {
-        debug(29, 1) ("AuthenticateNTLMHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver);
+    debug(29, 8) ("authenticateNTLMHandleReply: helper: '%p' sent us '%s'\n", lastserver, reply ? reply : "<NULL>");
+    valid = cbdataReferenceValid(data);
+
+    if (!valid) {
+        debug(29, 1) ("authenticateNTLMHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver);
         cbdataReferenceDone(r->data);
         authenticateStateFree(r);
-        debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", S_HELPER_RELEASE);
+        debug(29, 9) ("authenticateNTLMHandleReply: telling stateful helper : %d\n", S_HELPER_RELEASE);
         return S_HELPER_RELEASE;
     }
 
@@ -445,190 +370,78 @@ authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
         fatal("authenticateNTLMHandleReply: called with no result string\n");
     }
 
-    /* seperate out the useful data */
-    if (strncasecmp(reply, "TT ", 3) == 0) {
-        reply += 3;
-        /* we have been given a Challenge */
-        /* we should check we weren't given an empty challenge */
-        /* copy the challenge to the state data */
-        helperstate = static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(static_cast<helper_stateful_server *>(lastserver)));
-
-        if (helperstate == NULL)
-            fatal("lost NTLM helper state! quitting\n");
-
-        helperstate->challenge = xstrdup(reply);
-
-        helperstate->challengeuses = 0;
-
-        helperstate->renewed = squid_curtime;
-
-        /* and we satisfy the request that happended on the refresh boundary */
-        /* note this code is now in two places FIXME */
-        assert(r->auth_user_request != NULL);
-
-        assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
-
-        auth_user_request = r->auth_user_request;
-
-        ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
-
-        assert(ntlm_request != NULL);
+    auth_user_request = r->auth_user_request;
+    assert(auth_user_request != NULL);
+    ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
 
-        result = S_HELPER_DEFER;
+    assert(ntlm_request->waiting);
+    ntlm_request->waiting = 0;
+    safe_free(ntlm_request->client_blob);
 
-        /* reserve the server for future authentication */
-        ntlm_request->authserver_deferred = 1;
+    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());
 
-        debug(29, 9) ("authenticateNTLMHandleReply: helper '%p'\n", lastserver);
+    if (ntlm_request->authserver == NULL)
+        ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
+    else
+        assert(ntlm_request->authserver == lastserver);
 
-        assert(ntlm_request->auth_state == AUTHENTICATE_STATE_NEGOTIATE);
+    /* seperate out the useful data */
+    blob = strchr(reply, ' ');
 
-        ntlm_request->authserver = static_cast<helper_stateful_server *>(lastserver);
+    while (xisspace(*blob)) {    // trim leading spaces in blob
+        blob++;
+    }
 
-        ntlm_request->authchallenge = xstrdup(reply);
-    } else if (strncasecmp(reply, "AF ", 3) == 0) {
+    if (strncasecmp(reply, "TT ", 3) == 0 && blob != NULL) {
+        /* we have been given a blob to send to the client */
+        safe_free(ntlm_request->server_blob);
+        ntlm_request->server_blob = xstrdup(blob);
+        ntlm_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
+        auth_user_request->denyMessage("Authenication in progress");
+        debug(29, 4) ("authenticateNTLMHandleReply: Need to challenge the client with a server blob '%s'\n", blob);
+        result = S_HELPER_RESERVE;
+    } else if (strncasecmp(reply, "AF ", 3) == 0 && blob != NULL) {
         /* we're finished, release the helper */
-        reply += 3;
-        assert(r->auth_user_request != NULL);
-        assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
-        auth_user_request = r->auth_user_request;
-        ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
-        assert(ntlm_request);
-        auth_user = auth_user_request->user();
-        ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user());
-        assert(ntlm_user != NULL);
-        result = S_HELPER_RELEASE;
-        /* we only expect OK when finishing the handshake */
-        assert(ntlm_request->auth_state == AUTHENTICATE_STATE_RESPONSE);
-        ntlm_user->username(xstrdup(reply));
-        ntlm_request->authserver = NULL;
-#ifdef NTLM_FAIL_OPEN
-
-    } else if (strncasecmp(reply, "LD ", 3) == 0) {
-        /* This is a variant of BH, which rather than deny access
-         * allows the user through. The helper is starved and then refreshed
-         * via YR, all pending authentications are likely to fail also.
-         * It is meant for those helpers which occasionally fail for
-         * no reason at all (casus belli, NTLMSSP helper on NT domain,
-         * failing about 1 auth out of 1k.
-         * The code is a merge from the BH case with snippets of the AF
-         * case */
-        /* AF code: mark user as authenticated */
-        reply += 3;
-        assert(r->auth_user_request != NULL);
-        assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
-        auth_user_request = r->auth_user_request;
-        ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
-        assert(ntlm_request);
-        auth_user = auth_user_request->user();
-        ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user());
-        assert(ntlm_user != NULL);
-        result = S_HELPER_RELEASE;
-        /* we only expect LD when finishing the handshake */
-        assert(ntlm_request->auth_state == AUTHENTICATE_STATE_RESPONSE);
-        ntlm_user->username_ = xstrdup(reply);
-        helperstate = static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(ntlm_request->authserver));
-        ntlm_request->authserver = NULL;
-        /* BH code: mark helper as broken */
-        /* mark it for starving */
-        helperstate->starve = 1;
-#endif
+        ntlm_user->username(blob);
+        auth_user_request->denyMessage("Login successful");
+        safe_free(ntlm_request->server_blob);
+        authenticateNTLMReleaseServer(ntlm_request);
+        ntlm_request->auth_state = AUTHENTICATE_STATE_FINISHED;
 
-    } else if (strncasecmp(reply, "NA ", 3) == 0) {
-        /* TODO: only work with auth_user here if it exists */
-        assert(r->auth_user_request != NULL);
-        assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
-        auth_user_request = r->auth_user_request;
-        auth_user = auth_user_request->user();
-        assert(auth_user != NULL);
-        ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
-        ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
-        assert((ntlm_user != NULL) && (ntlm_request != NULL));
-        /* todo: action of Negotiate state on error */
-        result = S_HELPER_RELEASE;     /*some error has occured. no more requests */
-        ntlm_request->authserver = NULL;
-        debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
+        result = S_HELPER_RELEASE;
+        debug(29, 4) ("authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '%s'\n", blob);
+    } else if (strncasecmp(reply, "NA ", 3) == 0 && blob != NULL) {
+        /* authentication failure (wrong password, etc.) */
+        auth_user_request->denyMessage(blob);
         ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
-        reply += 3;
-
-        if (*reply)
-            auth_user_request->setDenyMessage(reply);
-    } else if (strncasecmp(reply, "NA", 2) == 0) {
-        /* NTLM Helper protocol violation! */
-        fatal("NTLM Helper returned invalid response \"NA\" - a error message MUST be attached\n");
+        safe_free(ntlm_request->server_blob);
+        authenticateNTLMReleaseServer(ntlm_request);
+        result = S_HELPER_RELEASE;
+        debug(29, 4) ("authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '%s'\n", 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 
+         * 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. */
-        assert(r->auth_user_request != NULL);
-        assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
-        auth_user_request = r->auth_user_request;
-        auth_user = auth_user_request->user();
-        assert(auth_user != NULL);
-        ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
-        ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
-        assert((ntlm_user != NULL) && (ntlm_request != NULL));
-        /*some error has occured. no more requests for
-                                                                                                                                        * this helper */
-        result = S_HELPER_RELEASE;
-        assert(ntlm_request->authserver ? ntlm_request->authserver == lastserver : 1);
-        helperstate = static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(ntlm_request->authserver));
-        ntlm_request->authserver = NULL;
-
-        if (ntlm_request->auth_state == AUTHENTICATE_STATE_NEGOTIATE) {
-            /* The helper broke on YR. It automatically
-             * resets */
-            debug(29, 1) ("authenticateNTLMHandleReply: Error obtaining challenge from helper: %p. Error returned '%s'\n", lastserver, reply);
-            /* mark it for starving */
-            helperstate->starve = 1;
-            /* resubmit the request. This helper is currently busy, so we will get
-             * a different one. Our auth state stays the same */
-            auth_user_request->start(r->handler, r->data);
-            /* don't call the callback */
-            cbdataReferenceDone(r->data);
-            authenticateStateFree(r);
-            debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result);
-            return result;
-        }
-
-        /* the helper broke on a KK */
-        /* first the standard KK stuff */
-        debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
-
-        /* now we mark the helper for resetting. */
-        helperstate->starve = 1;
-
+        auth_user_request->denyMessage(blob);
         ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
-
-        reply += 3;
-
-        if (*reply)
-            auth_user_request->setDenyMessage(reply);
+        safe_free(ntlm_request->server_blob);
+        authenticateNTLMReleaseServer(ntlm_request);
+        result = S_HELPER_RELEASE;
+        debug(29, 1) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
     } else {
-        /* TODO: only work with auth_user here if it exists */
-        /* TODO: take the request state into consideration */
-        assert(r->auth_user_request != NULL);
-        assert(r->auth_user_request->user()->auth_type == AUTH_NTLM);
-        auth_user_request = r->auth_user_request;
-        auth_user = auth_user_request->user();
-        assert(auth_user != NULL);
-        ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
-        ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
-        assert((ntlm_user != NULL) && (ntlm_request != NULL));
-        debug(29, 1) ("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
-        /* **** NOTE THIS CODE IS EFFECTIVELY UNTESTED **** */
-        /* restart the authentication process */
-        ntlm_request->auth_state = AUTHENTICATE_STATE_NONE;
-        assert(ntlm_request->authserver ? ntlm_request->authserver == lastserver : 1);
-        ntlm_request->authserver = NULL;
+        /* protocol error */
+        fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
     }
 
     r->handler(r->data, NULL);
     cbdataReferenceDone(r->data);
     authenticateStateFree(r);
-    debug(29, 9) ("NTLM HandleReply, telling stateful helper : %d\n", result);
+    debug(29, 9) ("authenticateNTLMHandleReply: telling stateful helper : %d\n", result);
     return result;
 }
 
@@ -639,229 +452,52 @@ authenticateNTLMStats(StoreEntry * sentry)
     helperStatefulStats(sentry, ntlmauthenticators);
 }
 
-/* is a particular challenge still valid ? */
-static int
-authenticateNTLMValidChallenge(ntlm_helper_state_t * helperstate)
-{
-    debug(29, 9) ("authenticateNTLMValidChallenge: Challenge is %s\n", helperstate->challenge ? "Valid" : "Invalid");
-
-    if (helperstate->challenge == NULL)
-        return 0;
-
-    return 1;
-}
-
-/* does our policy call for changing the challenge now? */
-static int
-authenticateNTLMChangeChallenge_p(ntlm_helper_state_t * helperstate)
-{
-    /* don't check for invalid challenges just for expiry choices */
-    /* this is needed because we have to starve the helper until all old
-     * requests have been satisfied */
-
-    if (!helperstate->renewed) {
-        /* first use, no challenge has been set. Without this check, it will
-         * loop forever */
-        debug(29, 5) ("authenticateNTLMChangeChallenge_p: first use\n");
-        return 0;
-    }
-
-    if (helperstate->challengeuses > ntlmConfig.challengeuses) {
-        debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge uses (%d) exceeded max uses (%d)\n", helperstate->challengeuses, ntlmConfig.challengeuses);
-        return 1;
-    }
-
-    if (helperstate->renewed + ntlmConfig.challengelifetime < squid_curtime) {
-        debug(29, 4) ("authenticateNTLMChangeChallenge_p: Challenge exceeded max lifetime by %d seconds\n", (int) (squid_curtime - (helperstate->renewed + ntlmConfig.challengelifetime)));
-        return 1;
-    }
-
-    debug(29, 9) ("Challenge is to be reused\n");
-    return 0;
-}
 
 /* send the initial data to a stateful ntlm authenticator module */
 void
 AuthNTLMUserRequest::module_start(RH * handler, void *data)
 {
     authenticateStateData *r = NULL;
-    helper_stateful_server *server;
-    ntlm_helper_state_t *helperstate;
-    char buf[8192];
-    char *sent_string = NULL;
+    static char buf[8192];
     ntlm_user_t *ntlm_user;
-    auth_user_t *auth_user;
+    auth_user_t *auth_user = user();
 
-    auth_user = this->user();
-    ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
-    assert(ntlm_user);
     assert(data);
+    assert(handler);
+    assert(auth_user);
     assert(auth_user->auth_type == AUTH_NTLM);
-    debug(29, 9) ("authenticateNTLMStart: auth state '%d'\n", auth_state);
 
-    switch (auth_state) {
-
-    case AUTHENTICATE_STATE_NEGOTIATE:
-        sent_string = ntlmnegotiate;
-        break;
+    ntlm_user = dynamic_cast<ntlm_user_t *>(user());
 
-    case AUTHENTICATE_STATE_RESPONSE:
-        sent_string = ntlmauthenticate;
-        assert(authserver);
-        debug(29, 9) ("authenticateNTLMStart: Asking NTLMauthenticator '%p'.\n", authserver);
-        break;
-
-    default:
-        fatal("Invalid authenticate state for NTLMStart");
-    }
-
-    while (xisgraph(*sent_string))     /*trim NTLM */
-        sent_string++;
-
-    while (xisspace(*sent_string))     /*trim leading spaces */
-        sent_string++;
-
-    debug(29, 9) ("authenticateNTLMStart: state '%d'\n", auth_state);
-
-    debug(29, 9) ("authenticateNTLMStart: '%s'\n", sent_string);
+    debug(29, 8) ("AuthNTLMUserRequest::module_start: auth state is '%d'\n", auth_state);
 
     if (ntlmConfig.authenticate == NULL) {
-        debug(29, 0) ("authenticateNTLMStart: no NTLM program specified:'%s'\n", sent_string);
+        debug(29, 0) ("AuthNTLMUserRequest::module_start: no NTLM program specified.");
         handler(data, NULL);
         return;
     }
 
-    /* this is ugly TODO: move the challenge generation routines to their own function and
-     * tidy the logic up to make use of the efficiency we now have */
-    switch (auth_state) {
+    r = cbdataAlloc(authenticateStateData);
+    r->handler = handler;
+    cbdataReference(data);
+    r->data = data;
+    r->auth_user_request = this;
 
-    case AUTHENTICATE_STATE_NEGOTIATE:
-        /*
-         * 1: get a helper server
-         * 2: does it have a challenge?
-         * 3: tell it to get a challenge, or give ntlmauthdone the challenge
-         */
-        server = helperStatefulDefer(ntlmauthenticators);
-        helperstate = server ? static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(server)) : NULL;
-
-        while ((server != NULL) && authenticateNTLMChangeChallenge_p(helperstate)) {
-            /* flag this helper for challenge changing */
-            helperstate->starve = 1;
-            /* and release the deferred request */
-            helperStatefulReleaseServer(server);
-            /* Get another deferrable server */
-            server = helperStatefulDefer(ntlmauthenticators);
-            helperstate = server ? static_cast<ntlm_helper_state_t *>(helperStatefulServerGetData(server)) : NULL;
-        }
-
-        if (server == NULL)
-            debug(29, 9) ("unable to get a deferred ntlm helper... all helpers are refreshing challenges. Queuing as a placeholder request.\n");
-
-        authserver = server;
-
-        /* tell the log what helper we have been given */
-        debug(29, 9) ("authenticateNTLMStart: helper '%p' assigned\n", server);
-
-        /* server and valid challenge? */
-        if ((server == NULL) || !authenticateNTLMValidChallenge(helperstate)) {
-            /* No server, or server with invalid challenge */
-            r = cbdataAlloc(authenticateStateData);
-            r->handler = handler;
-            r->data = cbdataReference(data);
-            r->auth_user_request = this;
-
-            lock()
-
-                ; /* locking myself */
-
-            if (server == NULL) {
-                helperStatefulSubmit(ntlmauthenticators, NULL, authenticateNTLMHandleplaceholder, r, NULL);
-            } else {
-                /* Server with invalid challenge */
-                snprintf(buf, 8192, "YR\n");
-                helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
-            }
-        } else {
-            /* (server != NULL and we have a valid challenge) */
-            /* TODO: turn the below into a function and call from here and handlereply */
-            /* increment the challenge uses */
-            helperstate->challengeuses++;
-            /* assign the challenge */
-            authchallenge = xstrdup(helperstate->challenge);
-            /* we're not actually submitting a request, so we need to release the helper
-             * should the connection close unexpectedly
-             */
-            authserver_deferred = 1;
-            handler(data, NULL);
-        }
-
-        break;
-
-    case AUTHENTICATE_STATE_RESPONSE:
-        r = cbdataAlloc(authenticateStateData);
-        r->handler = handler;
-        r->data = cbdataReference(data);
-        r->auth_user_request = this;
-
-        lock()
-
-            ;
-        snprintf(buf, 8192, "KK %s\n", sent_string);
-
-        /* getting rid of deferred request status */
-        authserver_deferred = 0;
-
-        helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
-
-        debug(29, 9) ("authenticateNTLMstart: finished\n");
-
-        break;
-
-    default:
-        fatal("Invalid authenticate state for NTLMStart");
-    }
-}
-
-/* callback used by stateful helper routines */
-static int
-authenticateNTLMHelperServerAvailable(void *data)
-{
-    ntlm_helper_state_t *statedata = static_cast<ntlm_helper_state_t *>(data);
+    lock()
 
-    if (statedata != NULL) {
-        if (statedata->starve) {
-            debug(29, 4) ("authenticateNTLMHelperServerAvailable: starving - returning 0\n");
-            return 0;
-        } else {
-            debug(29, 4) ("authenticateNTLMHelperServerAvailable: not starving - returning 1\n");
-            return 1;
-        }
+        ;
+    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);
     }
 
-    debug(29, 4) ("authenticateNTLMHelperServerAvailable: no state data - returning 0\n");
-    return 0;
-}
+    waiting = 1;
 
-static void
-authenticateNTLMHelperServerOnEmpty(void *data)
-{
-    ntlm_helper_state_t *statedata = static_cast<ntlm_helper_state_t *>(data);
-
-    if (statedata == NULL)
-        return;
-
-    if (statedata->starve) {
-        /* we have been starving the helper */
-        debug(29, 9) ("authenticateNTLMHelperServerOnEmpty: resetting challenge details\n");
-        statedata->starve = 0;
-        statedata->challengeuses = 0;
-        statedata->renewed = 0;
-        xfree(statedata->challenge);
-        statedata->challenge = NULL;
-    }
+    safe_free(client_blob);
+    helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, authserver);
 }
 
-
 /* clear the NTLM helper of being reserved for future requests */
 static void
 authenticateNTLMReleaseServer(auth_user_request_t * auth_user_request)
@@ -869,49 +505,45 @@ authenticateNTLMReleaseServer(auth_user_request_t * auth_user_request)
     AuthNTLMUserRequest *ntlm_request;
     assert(auth_user_request->user()->auth_type == AUTH_NTLM);
     ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
-    assert (ntlm_request);
     debug(29, 9) ("authenticateNTLMReleaseServer: releasing server '%p'\n", ntlm_request->authserver);
+    /* is it possible for the server to be NULL? hno seems to think so.
+     * Let's see what happens, might segfault in helperStatefulReleaseServer
+     * if it does. I leave it like this not to cover possibly problematic
+     * code-paths. Kinkie */
     helperStatefulReleaseServer(ntlm_request->authserver);
     ntlm_request->authserver = NULL;
 }
 
 /* clear any connection related authentication details */
 void
-AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn)
+AuthNTLMUserRequest::onConnectionClose(ConnStateData *connection)
 {
-    assert(conn != NULL);
+    assert(connection != NULL);
 
-    if (conn->auth_user_request != NULL) {
-        assert (conn->auth_user_request == this);
-        assert(this->conn == conn);
+    debug(29,8)("AuthNTLMUserRequest::onConnectionClose: closing connection '%p' (this is '%p')\n",connection,this);
 
-        if (authserver != NULL && authserver_deferred)
-            authenticateNTLMReleaseServer(this);
+    if (connection->auth_user_request == NULL) {
+        debug(29,8)("AuthNTLMUserRequest::onConnectionClose: no auth_user_request\n");
+        return;
+    }
 
-        /* unlock the connection based lock */
-        debug(29, 9) ("authenticateNTLMOnCloseConnection: Unlocking auth_user from the connection.\n");
+    if (authserver != NULL)
+        authenticateNTLMReleaseServer(this);
 
-        /* This still breaks the abstraction, but is at least read only now.
-        * If needed, this could be ignored, as the conn deletion will also unlock
-        * the auth user request.
-        */
-        this->unlock();
+    /* unlock the connection based lock */
+    debug(29, 9) ("AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '%p'.\n",connection);
 
-        conn->auth_user_request = NULL;
-    }
-}
+    /* This still breaks the abstraction, but is at least read only now.
+    * If needed, this could be ignored, as the conn deletion will also unlock
+    * the auth user request.
+    */
+    unlock();
 
-/* NTLMLastHeader: return a pointer to the last header used in authenticating
- * the request/conneciton
- */
-const char *
-AuthNTLMUserRequest::connLastHeader()
-{
-    return ntlmauthenticate;
+    connection->auth_user_request = NULL;
 }
 
 /*
- * Decode an NTLM [Proxy-]Auth string, placing the results in the passed
+ * Decode a NTLM [Proxy-]Auth string, placing the results in the passed
  * Auth_user structure.
  */
 AuthUserRequest *
@@ -925,54 +557,19 @@ AuthNTLMConfig::decode(char const *proxy_auth)
     auth_user_request->user()->addRequest(auth_user_request);
 
     /* all we have to do is identify that it's NTLM - the helper does the rest */
-    debug(29, 9) ("authenticateDecodeNTLMAuth: NTLM authentication\n");
+    debug(29, 9) ("AuthNTLMConfig::decode: NTLM authentication\n");
     return auth_user_request;
 }
 
-static int
-authenticateNTLMcmpUsername(ntlm_user_t * u1, ntlm_user_t * u2)
-{
-    return strcmp(u1->username(), u2->username());
-}
-
-
-/* there is a known race where a single client recieves the same challenge
- * and sends the same response to squid on a single select cycle.
- * Check for this and if found ignore the new link 
- */
-static void
-authenticateProxyAuthCacheAddLink(const char *key, auth_user_t * auth_user)
-{
-
-    struct ProxyAuthCachePointer *proxy_auth_hash;
-    dlink_node *node;
-    ntlm_user_t *ntlm_user;
-    ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
-    node = ntlm_user->proxy_auth_list.head;
-    /* prevent duplicates */
-
-    while (node) {
-
-        if (!strcmp(key, (char const *)((struct ProxyAuthCachePointer *) node->data)->key))
-            return;
-
-        node = node->next;
-    }
-
-    proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(ntlm_user_hash_pool->alloc());
-    proxy_auth_hash->key = xstrdup(key);
-    proxy_auth_hash->auth_user = auth_user;
-    dlinkAddTail(proxy_auth_hash, &proxy_auth_hash->link, &ntlm_user->proxy_auth_list);
-    hash_join(proxy_auth_cache, (hash_link *) proxy_auth_hash);
-}
-
 int
 AuthNTLMUserRequest::authenticated() const
 {
-    if (auth_state == AUTHENTICATE_STATE_DONE)
+    if (auth_state == AUTHENTICATE_STATE_FINISHED) {
+        debug(29, 9) ("AuthNTLMUserRequest::authenticated: user authenticated.\n");
         return 1;
+    }
 
-    debug(29, 9) ("User not fully authenticated.\n");
+    debug(29, 9) ("AuthNTLMUserRequest::authenticated: user not fully authenticated.\n");
 
     return 0;
 }
@@ -980,190 +577,157 @@ AuthNTLMUserRequest::authenticated() const
 void
 AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer conn, http_hdr_type type)
 {
-    const char *proxy_auth;
+    const char *proxy_auth, *blob;
 
-    struct ProxyAuthCachePointer *proxy_auth_hash = NULL;
+    //ProxyAuthCachePointer *proxy_auth_hash = NULL;
     auth_user_hash_pointer *usernamehash;
+
     /* TODO: rename this!! */
-    auth_user_t *auth_user;
-    AuthNTLMUserRequest *ntlm_request;
+    auth_user_t *local_auth_user;
     ntlm_user_t *ntlm_user;
-    LOCAL_ARRAY(char, ntlmhash, NTLM_CHALLENGE_SZ * 2);
-    /* get header */
-    proxy_auth = httpHeaderGetStr(&request->header, type);
 
-    auth_user = user();
-    assert(auth_user);
-    assert(auth_user->auth_type == AUTH_NTLM);
-    ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
-    ntlm_request = this;
-    assert (ntlm_request);
+    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.getRaw() == NULL) {
-        ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
-        debug(29, 1) ("authenticateNTLMAuthenticateUser: attempt to perform authentication without a connection!\n");
+        auth_state = AUTHENTICATE_STATE_FAILED;
+        debug(29, 1) ("AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!\n");
+        return;
+    }
+
+    if (waiting) {
+        debug(29, 1) ("AuthNTLMUserRequest::authenticate: waiting for helper reply!\n");
         return;
     }
 
-    switch (ntlm_request->auth_state) {
+    if (server_blob) {
+        debug(29,2)("AuthNTLMUserRequest::authenticate: need to challenge client '%s'!\n", server_blob);
+        return;
+    }
+
+    /* get header */
+    proxy_auth = httpHeaderGetStr(&request->header, type);
+
+    blob = proxy_auth + strlen("NTLM");
+
+    while (xisspace(*blob))     // trim leading spaces in blob
+        blob++;
+
+    switch (auth_state) {
 
     case AUTHENTICATE_STATE_NONE:
-        /* we've recieved a negotiate request. pass to a helper */
-        debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm none. %s\n", proxy_auth);
-        ntlm_request->auth_state = AUTHENTICATE_STATE_NEGOTIATE;
-        ntlm_request->ntlmnegotiate = xstrdup(proxy_auth);
+        /* we've recieved a ntlm request. pass to a helper */
+        debug(29, 9) ("AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '%s'\n", proxy_auth);
+        auth_state = AUTHENTICATE_STATE_INITIAL;
+        safe_free(client_blob);
+        client_blob=xstrdup(blob);
         conn->auth_type = AUTH_NTLM;
         conn->auth_user_request = this;
-        ntlm_request->conn = conn;
-        /* and lock for the connection duration */
-        debug(29, 9) ("authenticateNTLMAuthenticateUser: Locking auth_user from the connection.\n");
+        conn = conn;
 
-        this->lock()
+        lock()
 
-        ;
+            ;
         return;
 
         break;
 
-    case AUTHENTICATE_STATE_NEGOTIATE:
-        ntlm_request->auth_state = AUTHENTICATE_STATE_CHALLENGE;
-
-        /* We _MUST_ have the auth challenge by now */
-        assert(ntlm_request->authchallenge);
+    case AUTHENTICATE_STATE_INITIAL:
+        debug(29,1)("AuthNTLMUserRequest::authenticate: need to ask helper\n");
 
         return;
 
         break;
 
-    case AUTHENTICATE_STATE_CHALLENGE:
-        /* we should have recieved a NTLM challenge. pass it to the same
-         * helper process */
-        debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state challenge with header %s.\n", proxy_auth);
-
-        /* do a cache lookup here. If it matches it's a successful ntlm
-         * challenge - release the helper and use the existing auth_user 
-         * details. */
-
-        ntlm_request->ntlmauthenticate = xstrdup(proxy_auth);
 
-        /* cache entries have authenticateauthheaderchallengestring */
-        snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
-                 ntlm_request->ntlmauthenticate,
-                 ntlm_request->authchallenge);
+    case AUTHENTICATE_STATE_IN_PROGRESS:
+        /* we should have received a blob from the client. Hand it off to
+         * some helper */
+        safe_free(client_blob);
 
-        /* see if we already know this user's authenticate */
-        debug(29, 9) ("aclMatchProxyAuth: cache lookup with key '%s'\n", ntlmhash);
-
-        assert(proxy_auth_cache != NULL);
-
-        proxy_auth_hash = static_cast<ProxyAuthCachePointer *>(hash_lookup(proxy_auth_cache, ntlmhash));
-
-        if (!proxy_auth_hash) {        /* not in the hash table */
-            debug(29, 4) ("authenticateNTLMAuthenticateUser: proxy-auth cache miss.\n");
-            ntlm_request->auth_state = AUTHENTICATE_STATE_RESPONSE;
-            /* verify with the ntlm helper */
-        } else {
-            debug(29, 4) ("authenticateNTLMAuthenticateUser: ntlm proxy-auth cache hit\n");
-            /* throw away the temporary entry */
-            ntlm_request->authserver_deferred = 0;
-            authenticateNTLMReleaseServer(this);
-            authenticateAuthUserMerge(auth_user, proxy_auth_hash->auth_user);
-            auth_user = proxy_auth_hash->auth_user;
-            this->user(auth_user);
-            ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
-            /* we found one */
-            debug(29, 9) ("found matching cache entry\n");
-            assert(auth_user->auth_type == AUTH_NTLM);
-            /* get the existing entries details */
-            ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user);
-            debug(29, 9) ("Username to be used is %s\n", ntlm_user->username());
-            /* on ntlm auth we do not unlock the auth_user until the
-             * connection is dropped. Thank MS for this quirk */
-            auth_user->expiretime = current_time.tv_sec;
-        }
+        client_blob = xstrdup (blob);
 
         return;
+
         break;
 
-    case AUTHENTICATE_STATE_RESPONSE:
-        /* auth-challenge pair cache miss. We've just got the response from the helper */
-        /*add to cache and let them through */
-        ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
-        /* this connection is authenticated */
-        debug(29, 4) ("authenticated\nch    %s\nauth     %s\nauthuser %s\n",
-                      ntlm_request->authchallenge,
-                      ntlm_request->ntlmauthenticate,
-                      ntlm_user->username());
-        /* cache entries have authenticateauthheaderchallengestring */
-        snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
-                 ntlm_request->ntlmauthenticate,
-                 ntlm_request->authchallenge);
+    case AUTHENTICATE_STATE_FINISHED:
+        /* connection is authenticated */
+        debug(29, 4) ("AuthNTLMUserRequest::authenticate: authenticated user %s\n", ntlm_user->username());
+
         /* see if this is an existing user with a different proxy_auth
          * string */
-
-        if ((usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username())))
-           ) {
-            while ((usernamehash->user()->auth_type != auth_user->auth_type) && (usernamehash->next) && !authenticateNTLMcmpUsername(dynamic_cast<ntlm_user_t *>(usernamehash->user()), ntlm_user)
-                  )
-                usernamehash = static_cast<AuthUserHashPointer*>(usernamehash->next);
-            if (usernamehash->user()->auth_type == auth_user->auth_type) {
-                /*
-                 * add another link from the new proxy_auth to the
-                 * auth_user structure and update the information */
-                assert(proxy_auth_hash == NULL);
-                authenticateProxyAuthCacheAddLink(ntlmhash, usernamehash->user());
-                /* we can't seamlessly recheck the username due to the
-                 * challenge nature of the protocol. Just free the 
-                 * temporary auth_user */
-                authenticateAuthUserMerge(auth_user, usernamehash->user());
-                auth_user = usernamehash->user();
-                this->user(auth_user);
-            }
+        usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username()));
+
+        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();
+            _auth_user = local_auth_user;
         } else {
             /* store user in hash's */
-            auth_user->addToNameCache();
-            authenticateProxyAuthCacheAddLink(ntlmhash, auth_user);
+            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 */
-        auth_user->expiretime = current_time.tv_sec;
+        local_auth_user->expiretime = current_time.tv_sec;
+
+        authenticateNTLMReleaseServer(this);
+
+        auth_state = AUTHENTICATE_STATE_DONE;
+
         return;
+
         break;
 
     case AUTHENTICATE_STATE_DONE:
-        fatal("authenticateNTLMAuthenticateUser: unexpect auth state DONE! Report a bug to the squid developers.\n");
+        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 */
-        debug(29, 9) ("authenticateNTLMAuthenticateUser: auth state ntlm failed. %s\n", proxy_auth);
+        debug(29, 9) ("AuthNTLMUserRequest::authenticate: auth state ntlm failed. %s\n", proxy_auth);
+
         return;
+
+        break;
     }
 
     return;
 }
 
-AuthNTLMUserRequest::AuthNTLMUserRequest() : ntlmnegotiate(NULL), authchallenge(NULL), ntlmauthenticate(NULL),
-        authserver(NULL), auth_state(AUTHENTICATE_STATE_NONE),
-        authserver_deferred(0), conn(NULL), _theUser(NULL)
-{}
+AuthNTLMUserRequest::AuthNTLMUserRequest() :
+        conn(NULL), auth_state(AUTHENTICATE_STATE_NONE),
+        _theUser(NULL)
+{
+    waiting=0;
+    client_blob=0;
+    server_blob=0;
+    authserver=NULL;
+}
 
 AuthNTLMUserRequest::~AuthNTLMUserRequest()
 {
-    if (ntlmnegotiate)
-        xfree(ntlmnegotiate);
+    safe_free(server_blob);
+    safe_free(client_blob);
 
-    if (authchallenge)
-        xfree(authchallenge);
-
-    if (ntlmauthenticate)
-        xfree(ntlmauthenticate);
-
-    if (authserver != NULL && authserver_deferred) {
-        debug(29, 9) ("authenticateNTLMRequestFree: releasing server '%p'\n", authserver);
+    if (authserver != NULL) {
+        debug(29, 9) ("AuthNTLMUserRequest::~AuthNTLMUserRequest: releasing server '%p'\n", authserver);
         helperStatefulReleaseServer(authserver);
         authserver = NULL;
     }
@@ -1186,3 +750,9 @@ ntlmScheme::createConfig()
     return &ntlmConfig;
 }
 
+const char *
+AuthNTLMUserRequest::connLastHeader()
+{
+    return NULL;
+}
+
index 434c0c9d4497cb0f89bcc5cdc04a95e830468a80..28c9ca105b51aee4ee490f1ee8d1316faaa163d9 100644 (file)
@@ -14,9 +14,9 @@
 
 typedef enum {
     AUTHENTICATE_STATE_NONE,
-    AUTHENTICATE_STATE_NEGOTIATE,
-    AUTHENTICATE_STATE_CHALLENGE,
-    AUTHENTICATE_STATE_RESPONSE,
+    AUTHENTICATE_STATE_INITIAL,
+    AUTHENTICATE_STATE_IN_PROGRESS,
+    AUTHENTICATE_STATE_FINISHED,
     AUTHENTICATE_STATE_DONE,
     AUTHENTICATE_STATE_FAILED
 } auth_state_t;                 /* connection level auth state */
@@ -59,7 +59,6 @@ public:
     virtual void authenticate(HttpRequest * request, ConnStateData::Pointer conn, http_hdr_type type);
     virtual int module_direction();
     virtual void onConnectionClose(ConnStateData *);
-    virtual const char *connLastHeader();
     virtual void module_start(RH *, void *);
     virtual AuthUser *user() {return _theUser;}
 
@@ -67,21 +66,24 @@ public:
 
     virtual void user (AuthUser *aUser) {_theUser=dynamic_cast<NTLMUser *>(aUser);}
 
-    /* what negotiate string did the client use? */
-    char *ntlmnegotiate;
-    /* what challenge did we give the client? */
-    char *authchallenge;
-    /* what authenticate string did we get? */
-    char *ntlmauthenticate;
-    /*we need to store the NTLM server between requests */
+    virtual const char * connLastHeader();
+
+    /*we need to store the helper server between requests */
     helper_stateful_server *authserver;
-    /* how far through the authentication process are we? */
-    auth_state_t auth_state;
-    /* have we got the helper-server in a deferred state? */
-    int authserver_deferred;
     /* what connection is this associated with */
     ConnStateData::Pointer 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;
+
 private:
     /* the user */
     NTLMUser * _theUser;
@@ -89,14 +91,6 @@ private:
 
 MEMPROXY_CLASS_INLINE(AuthNTLMUserRequest)
 
-struct _ntlm_helper_state_t
-{
-    char *challenge;           /* the challenge to use with this helper */
-    int starve;                        /* 0= normal operation. 1=don't hand out any more challenges */
-    int challengeuses;         /* the number of times this challenge has been issued */
-    time_t renewed;
-};
-
 /* configuration runtime data */
 
 class AuthNTLMConfig : public AuthConfig
@@ -114,20 +108,10 @@ public:
     virtual void parse(AuthConfig *, int, char *);
     virtual const char * type() const;
     int authenticateChildren;
+    int keep_alive;
     wordlist *authenticate;
-    int challengeuses;
-    time_t challengelifetime;
 };
 
-struct ProxyAuthCachePointer : public hash_link
-{
-    dlink_node link;
-    /* other hash entries that point to the same auth_user */
-    auth_user_t *auth_user;
-};
-
-typedef struct _ntlm_helper_state_t ntlm_helper_state_t;
-
 typedef class AuthNTLMConfig auth_ntlm_config;
 
 #endif
index f214d37d08995b1c5ca06ed948ea2160f1145e0e..2634fa7994ed875f1e04465730d0b1cd3264e3a8 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: cache_cf.cc,v 1.481 2005/10/02 14:28:47 serassio Exp $
+ * $Id: cache_cf.cc,v 1.482 2005/10/23 11:55:33 hno Exp $
  *
  * DEBUG: section 3     Configuration File Parsing
  * AUTHOR: Harvest Derived
@@ -813,7 +813,7 @@ dump_acl(StoreEntry * entry, const char *name, ACL * ae)
                           name,
                           ae->name,
                           ae->typeString());
-        v = w = ae->dumpGeneric();
+        v = w = ae->dump();
 
         while (v != NULL) {
             debug(3, 3) ("dump_acl: %s %s %s\n", name, ae->name, v->key);
index fa6c35f1802999b316e728da6d97668875c1a442..c30d3d9185970ec18934027b7d0009ccb3f37fe8 100644 (file)
@@ -1,6 +1,6 @@
 
 #
-# $Id: cf.data.pre,v 1.400 2005/10/16 19:57:41 serassio Exp $
+# $Id: cf.data.pre,v 1.401 2005/10/23 11:55:33 hno Exp $
 #
 #
 # SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -1887,32 +1887,53 @@ DOC_START
        processes.
        auth_param ntlm children 5
 
-       "max_challenge_reuses" number
-       The maximum number of times a challenge given by a ntlm
-       authentication helper can be reused. Increasing this number
-       increases your exposure to replay attacks on your network.
-       0 means use the challenge only once.  (disable challenge
-       caching) See max_ntlm_challenge_lifetime for more information.
-       auth_param ntlm max_challenge_reuses 0
+       === Options for configuring the NEGOTIATE auth-scheme follow ===
 
-       "max_challenge_lifetime" timespan
-       The maximum time period a ntlm challenge is reused
-       over.  The actual period will be the minimum of this time
-       AND the number of reused challenges.
-       auth_param ntlm max_challenge_lifetime 2 minutes
+       "program" cmdline
+       Specify the command for the external Negotiate authenticator.
+       This protocol is used in Microsoft Active-Directory enabled setups with
+       the Microsoft Internet Explorer or Mozilla Firefox browsers.
+       Its main purpose is to exchange credentials with the Squid proxy
+       using the Kerberos mechanisms.
+       If you use a Negotiate authenticator, make sure you have at least one acl
+       of type proxy_auth active.  By default, the negotiate authenticator_program
+       is not used.
+       The only supported program for this role is the ntlm_auth
+       program distributed as part of Samba, version 3 or later.
+
+       auth_param negotiate program @DEFAULT_PREFIX@/bin/ntlm_auth --helper-protocol=gss-spnego
+
+       "children" numberofchildren
+       The number of authenticator processes to spawn (no default).
+       If you start too few Squid will have to wait for them to
+       process a backlog of credential verifications, slowing it
+       down. When crendential verifications are done via a (slow)
+       network you are likely to need lots of authenticator
+       processes.
+       auth_param negotiate children 5
+       
+       "keep_alive" on|off
+       If you experience problems with PUT/POST requests when using the
+       Negotiate authentication scheme then you can try setting this to
+       off. This will cause Squid to forcibly close the connection on
+       the initial requests where the browser asks which schemes are
+       supported by the proxy.
+
+       auth_param negotiate keep_alive on
 
 NOCOMMENT_START
 #Recommended minimum configuration:
+#auth_param ntlm program <uncomment and complete this line to activate>
+#auth_param ntlm children 5
+#auth_param negotiate program <uncomment and complete this line to activate>
+#auth_param negotiate children 5
+#auth_param negotiate keep_alive on
 #auth_param digest program <uncomment and complete this line>
 #auth_param digest children 5
 #auth_param digest realm Squid proxy-caching web server
 #auth_param digest nonce_garbage_interval 5 minutes
 #auth_param digest nonce_max_duration 30 minutes
 #auth_param digest nonce_max_count 50
-#auth_param ntlm program <uncomment and complete this line to activate>
-#auth_param ntlm children 5
-#auth_param ntlm max_challenge_reuses 0
-#auth_param ntlm max_challenge_lifetime 2 minutes
 #auth_param basic program <uncomment and complete this line>
 auth_param basic children 5
 auth_param basic realm Squid proxy-caching web server
index 09bd6746ab481ba40a882a024e5ce680545820ce..e0272a89994ef96049358074f55774c474074af0 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: client_side.cc,v 1.698 2005/09/17 05:50:08 wessels Exp $
+ * $Id: client_side.cc,v 1.699 2005/10/23 11:55:36 hno Exp $
  *
  * DEBUG: section 33    Client-side Routines
  * AUTHOR: Duane Wessels
@@ -598,8 +598,11 @@ ConnStateData::close()
     assert(areAllContextsForThisConnection());
     freeAllContexts();
 
-    if (auth_user_request != NULL)
+    if (auth_user_request != NULL) {
+        debug(33,4)("ConnStateData::close: freeing auth_user_request '%p' (this is '%p')\n",
+                    auth_user_request,this);
         auth_user_request->onConnectionClose(this);
+    }
 }
 
 bool
index 3c2ff076f0051c3b45991396d7d2c6cda4f4c4ec..509fe4547e78aa8934ddc5166a1d0c697dd219b2 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: enums.h,v 1.243 2005/08/28 08:55:21 serassio Exp $
+ * $Id: enums.h,v 1.244 2005/10/23 11:55:36 hno Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -511,6 +511,7 @@ typedef enum {
     AUTH_BASIC,
     AUTH_NTLM,
     AUTH_DIGEST,
+    AUTH_NEGOTIATE,
     AUTH_BROKEN                        /* known type, but broken data */
 } auth_type_t;
 
index fc13584a6a256050a48abb08c218dd54c21c4073..b55c5f20f893bafb76d1a44312b8eb7b41c5d995 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: helper.cc,v 1.69 2005/09/17 05:50:08 wessels Exp $
+ * $Id: helper.cc,v 1.70 2005/10/23 11:55:37 hno Exp $
  *
  * DEBUG: section 84    Helper process maintenance
  * AUTHOR: Harvest Derived?
@@ -935,6 +935,10 @@ helperHandleRead(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, voi
         char *msg = srv->rbuf;
         int i = 0;
         debug(84, 3) ("helperHandleRead: end of reply found\n");
+
+        if (t > srv->rbuf && t[-1] == '\r')
+            t[-1] = '\0';
+
         *t++ = '\0';
 
         if (hlp->concurrency) {
@@ -1029,6 +1033,10 @@ helperStatefulHandleRead(int fd, char *buf, size_t len, comm_err_t flag, int xer
     if ((t = strchr(srv->rbuf, '\n'))) {
         /* end of reply found */
         debug(84, 3) ("helperStatefulHandleRead: end of reply found\n");
+
+        if (t > srv->rbuf && t[-1] == '\r')
+            t[-1] = '\0';
+
         *t = '\0';
 
         if (cbdataReferenceValid(r->data)) {
diff --git a/test-suite/negotiate_test.sh b/test-suite/negotiate_test.sh
new file mode 100644 (file)
index 0000000..1c2160e
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+while read request; do
+data="`echo $request | cut -c4-`"
+blob="$$.$data-$challenge.`date +%s`"
+case $request in
+
+??" USER="*)
+       echo "AF Success-$blob `echo $request|cut -d= -f2-`"
+       ;;
+
+??" BAD"*)
+       echo "BH `echo $request|cut -c7-`"
+       ;;
+
+??" ERR"*)
+       echo "NA Invalid-$blob `echo $request|cut -c7-`"
+       ;;
+
+"YR"*)
+       challenge="$data.`date +%s`"
+       echo "TT Challenge-$$.$challenge *"
+       ;;
+
+"KK"*)
+       echo "TT Negotiate-$$.$data-$challenge.`date +%s` *"
+       ;;
+*)
+       echo "BH Invalid request"
+       ;;
+esac
+done
diff --git a/test-suite/ntlm_test.sh b/test-suite/ntlm_test.sh
new file mode 100644 (file)
index 0000000..d49a512
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+while read request; do
+data="`echo $request | cut -c4-`"
+blob="$$.$data-$challenge.`date +%s`"
+case $request in
+
+??" USER="*)
+       echo "AF `echo $request|cut -d= -f2-`"
+       ;;
+
+??" BAD"*)
+       echo "BH `echo $request|cut -c7-`"
+       ;;
+
+??" ERR"*)
+       echo "NA `echo $request|cut -c7-`"
+       ;;
+
+"YR"*)
+       challenge="$data.`date +%s`"
+       echo "TT Challenge-$$.$challenge"
+       ;;
+
+"KK"*)
+       echo "TT Negotiate-$$.$data-$challenge.`date +%s`"
+       ;;
+*)
+       echo "BH Invalid request"
+       ;;
+esac
+done
diff --git a/test-suite/run_negotiate_test.sh b/test-suite/run_negotiate_test.sh
new file mode 100644 (file)
index 0000000..1867084
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+while read auth; do
+       echo "HEAD http://www.squid-cache.org/ HTTP/1.0"
+       if [ -n "$auth" ]; then
+               echo "Proxy-Authorization: Negotiate $auth"
+       fi
+       echo "Proxy-Connection: keep-alive"
+       echo
+done | tee -a /dev/fd/2 | nc localhost 3128
diff --git a/test-suite/run_ntlm_test.sh b/test-suite/run_ntlm_test.sh
new file mode 100644 (file)
index 0000000..4b7212b
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+while read auth; do
+       echo "HEAD http://www.squid-cache.org/ HTTP/1.0"
+       if [ -n "$auth" ]; then
+               echo "Proxy-Authorization: NTLM $auth"
+       fi
+       echo "Proxy-Connection: keep-alive"
+       echo
+done | tee -a /dev/fd/2 | nc localhost 3128