]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/auth/ntlm/auth_ntlm.cc
Cleanup: zap CVS Id tags
[thirdparty/squid.git] / src / auth / ntlm / auth_ntlm.cc
index 1f97db93b46948134129af673e1d7ea2aa8c164b..6b0caeb363da4df8df745fdeb0e3d565991676e2 100644 (file)
@@ -1,6 +1,5 @@
-
 /*
- * $Id: auth_ntlm.cc,v 1.50 2005/10/23 11:55:38 hno Exp $
+ * $Id$
  *
  * DEBUG: section 29    NTLM Authenticator
  * AUTHOR: Robert Collins, Henrik Nordstrom, Francesco Chemolli
  *  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 "squid.h"
 #include "auth_ntlm.h"
 #include "authenticate.h"
+#include "CacheManager.h"
 #include "Store.h"
 #include "client_side.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 /* TODO remove this include */
 #include "ntlmScheme.h"
+#include "wordlist.h"
+#include "SquidTime.h"
 
 static void
-authenticateNTLMReleaseServer(auth_user_request_t * auth_user_request);
+authenticateNTLMReleaseServer(AuthUserRequest * auth_user_request);
 
 
 static void
 authenticateStateFree(authenticateStateData * r)
 {
+    AUTHUSERREQUESTUNLOCK(r->auth_user_request, "r");
     cbdataFree(r);
 }
 
@@ -68,8 +71,6 @@ CBDATA_TYPE(authenticateStateData);
 
 static int authntlm_initialised = 0;
 
-//static MemAllocatorProxy *ntlm_user_hash_pool = NULL;
-
 static auth_ntlm_config ntlmConfig;
 
 static hash_table *proxy_auth_cache = NULL;
@@ -85,7 +86,7 @@ void
 ntlmScheme::done()
 {
     /* TODO: this should be a Config call. */
-    debug(29, 2) ("ntlmScheme::done: shutting down NTLM authentication.\n");
+    debugs(29, 2, "ntlmScheme::done: shutting down NTLM authentication.");
 
     if (ntlmauthenticators)
         helperStatefulShutdown(ntlmauthenticators);
@@ -100,7 +101,7 @@ ntlmScheme::done()
 
     ntlmauthenticators = NULL;
 
-    debug(29, 2) ("ntlmScheme::done: NTLM authentication Shutdown.\n");
+    debugs(29, 2, "ntlmScheme::done: NTLM authentication Shutdown.");
 }
 
 /* free any allocated configuration details */
@@ -146,7 +147,7 @@ AuthNTLMConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
     } else if (strcasecmp(param_str, "keep_alive") == 0) {
         parse_onoff(&keep_alive);
     } else {
-        debug(28, 0) ("AuthNTLMConfig::parse: unrecognised ntlm auth scheme parameter '%s'\n", param_str);
+        debugs(29, 0, "AuthNTLMConfig::parse: unrecognised ntlm auth scheme parameter '" << param_str << "'");
     }
 
     /*
@@ -172,16 +173,7 @@ AuthNTLMConfig::type() const
 void
 AuthNTLMConfig::init(AuthConfig * scheme)
 {
-    static unsigned char ntlm_was_already_initialised = 0;
-
     if (authenticate) {
-#if PLACEHOLDER
-
-        if (!ntlm_user_hash_pool)
-
-            ntlm_user_hash_pool = new MemAllocatorProxy("NTLM Header Hash Data", sizeof(struct ProxyAuthCachePointer));
-
-#endif
 
         authntlm_initialised = 1;
 
@@ -201,17 +193,19 @@ AuthNTLMConfig::init(AuthConfig * scheme)
 
         helperStatefulOpenServers(ntlmauthenticators);
 
-        if (!ntlm_was_already_initialised) {
-            cachemgrRegister("ntlmauthenticator",
-                             "NTLM User Authenticator Stats",
-                             authenticateNTLMStats, 0, 1);
-            ntlm_was_already_initialised++;
-        }
-
         CBDATA_INIT_TYPE(authenticateStateData);
     }
 }
 
+void
+AuthNTLMConfig::registerWithCacheManager(void)
+{
+    CacheManager::GetInstance()->
+    registerAction("ntlmauthenticator",
+                   "NTLM User Authenticator Stats",
+                   authenticateNTLMStats, 0, 1);
+}
+
 bool
 AuthNTLMConfig::active() const
 {
@@ -222,11 +216,11 @@ bool
 AuthNTLMConfig::configured() const
 {
     if ((authenticate != NULL) && (authenticateChildren != 0)) {
-        debug(29, 9) ("AuthNTLMConfig::configured: returning configured\n");
+        debugs(29, 9, "AuthNTLMConfig::configured: returning configured");
         return true;
     }
 
-    debug(29, 9) ("AuthNTLMConfig::configured: returning unconfigured\n");
+    debugs(29, 9, "AuthNTLMConfig::configured: returning unconfigured");
     return false;
 }
 
@@ -245,7 +239,7 @@ AuthNTLMUserRequest::module_direction()
         /* no progress at all. */
 
     case AUTHENTICATE_STATE_NONE:
-        debug(29, 1) ("AuthNTLMUserRequest::direction: called before NTLM Authenticate for request %p!. Report a bug to squid-dev.\n",this);
+        debugs(29, 1, "AuthNTLMUserRequest::direction: called before NTLM Authenticate for request " << this << "!. Report a bug to squid-dev.");
         return -2; /* error */
 
     case AUTHENTICATE_STATE_FAILED:
@@ -256,14 +250,11 @@ AuthNTLMUserRequest::module_direction()
         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) ("AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL\n");
+        debugs(29, 1, "AuthNTLMUserRequest::direction: Unexpected AUTHENTICATE_STATE_INITIAL");
         return -2;
     }
 
@@ -271,39 +262,40 @@ AuthNTLMUserRequest::module_direction()
 }
 
 void
-AuthNTLMConfig::fixHeader(auth_user_request_t *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request)
+AuthNTLMConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request)
 {
     AuthNTLMUserRequest *ntlm_request;
 
-    if (!request->flags.proxy_keepalive)
+    if (!authenticate)
         return;
 
-    if (!authenticate)
+    /* Need keep-alive */
+    if (!request->flags.proxy_keepalive && request->flags.must_keepalive)
         return;
 
     /* New request, no user details */
     if (auth_user_request == NULL) {
-        debug(29, 9) ("AuthNTLMConfig::fixHeader: Sending type:%d header: 'NTLM'\n", type);
+        debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM'");
         httpHeaderPutStrf(&rep->header, type, "NTLM");
 
         if (!keep_alive) {
             /* drop the connection */
-            httpHeaderDelByName(&rep->header, "keep-alive");
             request->flags.proxy_keepalive = 0;
         }
     } else {
         ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
 
+        assert(ntlm_request != NULL);
+
         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 */
 
-        case AUTHENTICATE_STATE_FINISHED:
+        case AUTHENTICATE_STATE_DONE:
             /* Special case: authentication finished OK but disallowed by ACL.
              * Need to start over to give the client another chance.
              */
@@ -312,21 +304,20 @@ AuthNTLMConfig::fixHeader(auth_user_request_t *auth_user_request, HttpReply *rep
         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);
+            debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM'");
             httpHeaderPutStrf(&rep->header, type, "NTLM");
             break;
 
         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);
+            debugs(29, 9, "AuthNTLMConfig::fixHeader: Sending type:" << type << " header: 'NTLM " << 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;
 
 
         default:
-            debug(29, 0) ("AuthNTLMConfig::fixHeader: state %d.\n", ntlm_request->auth_state);
+            debugs(29, 0, "AuthNTLMConfig::fixHeader: state " << ntlm_request->auth_state << ".");
             fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
         }
     }
@@ -334,7 +325,7 @@ AuthNTLMConfig::fixHeader(auth_user_request_t *auth_user_request, HttpReply *rep
 
 NTLMUser::~NTLMUser()
 {
-    debug(29, 5) ("NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '%p'\n",this);
+    debugs(29, 5, "NTLMUser::~NTLMUser: doing nothing to clearNTLM scheme data for '" << this << "'");
 }
 
 static stateful_helper_callback_t
@@ -346,34 +337,32 @@ authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
     stateful_helper_callback_t result = S_HELPER_UNKNOWN;
     char *blob;
 
-    auth_user_request_t *auth_user_request;
+    AuthUserRequest *auth_user_request;
     AuthUser *auth_user;
     NTLMUser *ntlm_user;
     AuthNTLMUserRequest *ntlm_request;
 
-    debug(29, 8) ("authenticateNTLMHandleReply: helper: '%p' sent us '%s'\n", lastserver, reply ? reply : "<NULL>");
-    valid = cbdataReferenceValid(data);
+    debugs(29, 8, "authenticateNTLMHandleReply: helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
+    valid = cbdataReferenceValid(r->data);
 
     if (!valid) {
-        debug(29, 1) ("authenticateNTLMHandleReply: invalid callback data. Releasing helper '%p'.\n", lastserver);
+        debugs(29, 1, "authenticateNTLMHandleReply: invalid callback data. Releasing helper '" << lastserver << "'.");
         cbdataReferenceDone(r->data);
         authenticateStateFree(r);
-        debug(29, 9) ("authenticateNTLMHandleReply: telling stateful helper : %d\n", S_HELPER_RELEASE);
+        debugs(29, 9, "authenticateNTLMHandleReply: telling stateful helper : " << 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("authenticateNTLMHandleReply: called with no result string\n");
+        debugs(29, 1, "authenticateNTLMHandleReply: Helper '" << lastserver << "' crashed!.");
+        reply = (char *)"BH Internal error";
     }
 
     auth_user_request = r->auth_user_request;
     assert(auth_user_request != NULL);
     ntlm_request = dynamic_cast<AuthNTLMUserRequest *>(auth_user_request);
 
+    assert(ntlm_request != NULL);
     assert(ntlm_request->waiting);
     ntlm_request->waiting = 0;
     safe_free(ntlm_request->client_blob);
@@ -383,6 +372,8 @@ authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
     assert(auth_user->auth_type == AUTH_NTLM);
     ntlm_user = dynamic_cast<ntlm_user_t *>(auth_user_request->user());
 
+    assert(ntlm_user != NULL);
+
     if (ntlm_request->authserver == NULL)
         ntlm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
     else
@@ -391,36 +382,66 @@ authenticateNTLMHandleReply(void *data, void *lastserver, char *reply)
     /* seperate out the useful data */
     blob = strchr(reply, ' ');
 
-    while (xisspace(*blob)) {    // trim leading spaces in blob
+    if (blob)
         blob++;
-    }
 
-    if (strncasecmp(reply, "TT ", 3) == 0 && blob != NULL) {
+    if (strncasecmp(reply, "TT ", 3) == 0) {
         /* we have been given a blob to send to the client */
         safe_free(ntlm_request->server_blob);
-        ntlm_request->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) {
+        ntlm_request->request->flags.must_keepalive = 1;
+        if (ntlm_request->request->flags.proxy_keepalive) {
+            ntlm_request->server_blob = xstrdup(blob);
+            ntlm_request->auth_state = AUTHENTICATE_STATE_IN_PROGRESS;
+            auth_user_request->denyMessage("Authentication in progress");
+            debugs(29, 4, "authenticateNTLMHandleReply: Need to challenge the client with a server blob '" << blob << "'");
+            result = S_HELPER_RESERVE;
+        } else {
+            ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
+            auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
+            result = S_HELPER_RELEASE;
+        }
+    } else if (strncasecmp(reply, "AF ", 3) == 0) {
         /* we're finished, release the helper */
         ntlm_user->username(blob);
         auth_user_request->denyMessage("Login successful");
         safe_free(ntlm_request->server_blob);
-        authenticateNTLMReleaseServer(ntlm_request);
-        ntlm_request->auth_state = AUTHENTICATE_STATE_FINISHED;
 
         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) {
+        debugs(29, 4, "authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '" << blob << "'");
+        /* connection is authenticated */
+        debugs(29, 4, "AuthNTLMUserRequest::authenticate: authenticated user " << ntlm_user->username());
+        /* see if this is an existing user with a different proxy_auth
+         * string */
+        auth_user_hash_pointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, ntlm_user->username()));
+        AuthUser *local_auth_user = ntlm_request->user();
+        while (usernamehash && (usernamehash->user()->auth_type != AUTH_NTLM || strcmp(usernamehash->user()->username(), ntlm_user->username()) != 0))
+            usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
+        if (usernamehash) {
+            /* we can't seamlessly recheck the username due to the
+             * challenge-response nature of the protocol.
+             * Just free the temporary auth_user */
+            usernamehash->user()->absorb(local_auth_user);
+            //authenticateAuthUserMerge(local_auth_user, usernamehash->user());
+            local_auth_user = usernamehash->user();
+            ntlm_request->_auth_user = local_auth_user;
+        } else {
+            /* store user in hash's */
+            local_auth_user->addToNameCache();
+            // authenticateUserNameCacheAdd(local_auth_user);
+        }
+        /* set these to now because this is either a new login from an
+         * existing user or a new user */
+        local_auth_user->expiretime = current_time.tv_sec;
+        authenticateNTLMReleaseServer(ntlm_request);
+        ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
+    } else if (strncasecmp(reply, "NA ", 3) == 0) {
         /* authentication failure (wrong password, etc.) */
         auth_user_request->denyMessage(blob);
         ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
         safe_free(ntlm_request->server_blob);
         authenticateNTLMReleaseServer(ntlm_request);
         result = S_HELPER_RELEASE;
-        debug(29, 4) ("authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '%s'\n", blob);
+        debugs(29, 4, "authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '" << blob << "'");
     } else if (strncasecmp(reply, "BH ", 3) == 0) {
         /* TODO kick off a refresh process. This can occur after a YR or after
          * a KK. If after a YR release the helper and resubmit the request via
@@ -432,24 +453,27 @@ authenticateNTLMHandleReply(void *data, void *lastserver, char *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);
+        debugs(29, 1, "authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '" << reply << "'");
     } else {
         /* protocol error */
         fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
     }
 
+    if (ntlm_request->request) {
+        HTTPMSGUNLOCK(ntlm_request->request);
+        ntlm_request->request = NULL;
+    }
     r->handler(r->data, NULL);
     cbdataReferenceDone(r->data);
     authenticateStateFree(r);
-    debug(29, 9) ("authenticateNTLMHandleReply: telling stateful helper : %d\n", result);
+    debugs(29, 9, "authenticateNTLMHandleReply: telling stateful helper : " << result);
     return result;
 }
 
 static void
 authenticateNTLMStats(StoreEntry * sentry)
 {
-    storeAppendPrintf(sentry, "NTLM Authenticator Statistics:\n");
-    helperStatefulStats(sentry, ntlmauthenticators);
+    helperStatefulStats(sentry, ntlmauthenticators, "NTLM Authenticator Statistics");
 }
 
 
@@ -460,7 +484,7 @@ AuthNTLMUserRequest::module_start(RH * handler, void *data)
     authenticateStateData *r = NULL;
     static char buf[8192];
     ntlm_user_t *ntlm_user;
-    auth_user_t *auth_user = user();
+    AuthUser *auth_user = user();
 
     assert(data);
     assert(handler);
@@ -469,23 +493,20 @@ AuthNTLMUserRequest::module_start(RH * handler, void *data)
 
     ntlm_user = dynamic_cast<ntlm_user_t *>(user());
 
-    debug(29, 8) ("AuthNTLMUserRequest::module_start: auth state is '%d'\n", auth_state);
+    debugs(29, 8, "AuthNTLMUserRequest::module_start: auth state is '" << auth_state << "'");
 
     if (ntlmConfig.authenticate == NULL) {
-        debug(29, 0) ("AuthNTLMUserRequest::module_start: no NTLM program specified.");
+        debugs(29, 0, "AuthNTLMUserRequest::module_start: no NTLM program specified.");
         handler(data, NULL);
         return;
     }
 
     r = cbdataAlloc(authenticateStateData);
     r->handler = handler;
-    cbdataReference(data);
-    r->data = data;
+    r->data = cbdataReference(data);
     r->auth_user_request = this;
+    AUTHUSERREQUESTLOCK(r->auth_user_request, "r");
 
-    lock()
-
-        ;
     if (auth_state == AUTHENTICATE_STATE_INITIAL) {
         snprintf(buf, 8192, "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
     } else {
@@ -500,30 +521,35 @@ AuthNTLMUserRequest::module_start(RH * handler, void *data)
 
 /* clear the NTLM helper of being reserved for future requests */
 static void
-authenticateNTLMReleaseServer(auth_user_request_t * auth_user_request)
+authenticateNTLMReleaseServer(AuthUserRequest * auth_user_request)
 {
     AuthNTLMUserRequest *ntlm_request;
     assert(auth_user_request->user()->auth_type == AUTH_NTLM);
     ntlm_request = dynamic_cast< AuthNTLMUserRequest *>(auth_user_request);
-    debug(29, 9) ("authenticateNTLMReleaseServer: releasing server '%p'\n", ntlm_request->authserver);
+    debugs(29, 9, "authenticateNTLMReleaseServer: releasing server '" << 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;
+    /* DPW 2007-05-07
+     * yes, it is possible */
+    assert(ntlm_request != NULL);
+    if (ntlm_request->authserver) {
+        helperStatefulReleaseServer(ntlm_request->authserver);
+        ntlm_request->authserver = NULL;
+    }
 }
 
 /* clear any connection related authentication details */
 void
-AuthNTLMUserRequest::onConnectionClose(ConnStateData *connection)
+AuthNTLMUserRequest::onConnectionClose(ConnStateData *conn)
 {
-    assert(connection != NULL);
+    assert(conn != NULL);
 
-    debug(29,8)("AuthNTLMUserRequest::onConnectionClose: closing connection '%p' (this is '%p')\n",connection,this);
+    debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: closing connection '" << conn << "' (this is '" << this << "')");
 
-    if (connection->auth_user_request == NULL) {
-        debug(29,8)("AuthNTLMUserRequest::onConnectionClose: no auth_user_request\n");
+    if (conn->auth_user_request == NULL) {
+        debugs(29, 8, "AuthNTLMUserRequest::onConnectionClose: no auth_user_request");
         return;
     }
 
@@ -531,15 +557,9 @@ AuthNTLMUserRequest::onConnectionClose(ConnStateData *connection)
         authenticateNTLMReleaseServer(this);
 
     /* unlock the connection based lock */
-    debug(29, 9) ("AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '%p'.\n",connection);
+    debugs(29, 9, "AuthNTLMUserRequest::onConnectionClose: Unlocking auth_user from the connection '" << conn << "'.");
 
-    /* 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;
+    AUTHUSERREQUESTUNLOCK(conn->auth_user_request, "conn");
 }
 
 /*
@@ -557,33 +577,30 @@ 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) ("AuthNTLMConfig::decode: NTLM authentication\n");
+    debugs(29, 9, "AuthNTLMConfig::decode: NTLM authentication");
     return auth_user_request;
 }
 
 int
 AuthNTLMUserRequest::authenticated() const
 {
-    if (auth_state == AUTHENTICATE_STATE_FINISHED) {
-        debug(29, 9) ("AuthNTLMUserRequest::authenticated: user authenticated.\n");
+    if (auth_state == AUTHENTICATE_STATE_DONE) {
+        debugs(29, 9, "AuthNTLMUserRequest::authenticated: user authenticated.");
         return 1;
     }
 
-    debug(29, 9) ("AuthNTLMUserRequest::authenticated: user not fully authenticated.\n");
+    debugs(29, 9, "AuthNTLMUserRequest::authenticated: user not fully authenticated.");
 
     return 0;
 }
 
 void
-AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer conn, http_hdr_type type)
+AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData * 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;
+    AuthUser *local_auth_user;
     ntlm_user_t *ntlm_user;
 
     local_auth_user = user();
@@ -595,51 +612,60 @@ AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer
     /* Check that we are in the client side, where we can generate
      * auth challenges */
 
-    if (conn.getRaw() == NULL) {
+    if (conn == NULL || !cbdataReferenceValid(conn)) {
         auth_state = AUTHENTICATE_STATE_FAILED;
-        debug(29, 1) ("AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!\n");
+        debugs(29, 1, "AuthNTLMUserRequest::authenticate: attempt to perform authentication without a connection!");
         return;
     }
 
     if (waiting) {
-        debug(29, 1) ("AuthNTLMUserRequest::authenticate: waiting for helper reply!\n");
+        debugs(29, 1, "AuthNTLMUserRequest::authenticate: waiting for helper reply!");
         return;
     }
 
     if (server_blob) {
-        debug(29,2)("AuthNTLMUserRequest::authenticate: need to challenge client '%s'!\n", server_blob);
+        debugs(29, 2, "AuthNTLMUserRequest::authenticate: need to challenge client '" << server_blob << "'!");
         return;
     }
 
     /* get header */
-    proxy_auth = httpHeaderGetStr(&request->header, type);
+    proxy_auth = request->header.getStr(type);
 
-    blob = proxy_auth + strlen("NTLM");
+    /* locate second word */
+    blob = proxy_auth;
 
-    while (xisspace(*blob))     // trim leading spaces in blob
-        blob++;
+    /* if proxy_auth is actually NULL, we'd better not manipulate it. */
+    if (blob) {
+        while (xisspace(*blob) && *blob)
+            blob++;
+
+        while (!xisspace(*blob) && *blob)
+            blob++;
+
+        while (xisspace(*blob) && *blob)
+            blob++;
+    }
 
     switch (auth_state) {
 
     case AUTHENTICATE_STATE_NONE:
-        /* we've recieved a ntlm request. pass to a helper */
-        debug(29, 9) ("AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '%s'\n", proxy_auth);
+        /* we've received a ntlm request. pass to a helper */
+        debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm none. Received blob: '" << proxy_auth << "'");
         auth_state = AUTHENTICATE_STATE_INITIAL;
         safe_free(client_blob);
         client_blob=xstrdup(blob);
         conn->auth_type = AUTH_NTLM;
+        assert(conn->auth_user_request == NULL);
         conn->auth_user_request = this;
-        conn = conn;
-
-        lock()
-
-            ;
+        AUTHUSERREQUESTLOCK(conn->auth_user_request, "conn");
+        this->request = request;
+        HTTPMSGLOCK(this->request);
         return;
 
         break;
 
     case AUTHENTICATE_STATE_INITIAL:
-        debug(29,1)("AuthNTLMUserRequest::authenticate: need to ask helper\n");
+        debugs(29, 1, "AuthNTLMUserRequest::authenticate: need to ask helper");
 
         return;
 
@@ -653,43 +679,10 @@ AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer
 
         client_blob = xstrdup (blob);
 
-        return;
-
-        break;
-
-    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 */
-        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 */
-            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;
-
-        authenticateNTLMReleaseServer(this);
-
-        auth_state = AUTHENTICATE_STATE_DONE;
-
+        if (this->request)
+            HTTPMSGUNLOCK(this->request);
+        this->request = request;
+        HTTPMSGLOCK(this->request);
         return;
 
         break;
@@ -701,7 +694,7 @@ AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer
 
     case AUTHENTICATE_STATE_FAILED:
         /* we've failed somewhere in authentication */
-        debug(29, 9) ("AuthNTLMUserRequest::authenticate: auth state ntlm failed. %s\n", proxy_auth);
+        debugs(29, 9, "AuthNTLMUserRequest::authenticate: auth state ntlm failed. " << proxy_auth);
 
         return;
 
@@ -712,13 +705,14 @@ AuthNTLMUserRequest::authenticate(HttpRequest * request, ConnStateData::Pointer
 }
 
 AuthNTLMUserRequest::AuthNTLMUserRequest() :
-        conn(NULL), auth_state(AUTHENTICATE_STATE_NONE),
+        /*conn(NULL),*/ auth_state(AUTHENTICATE_STATE_NONE),
         _theUser(NULL)
 {
     waiting=0;
     client_blob=0;
     server_blob=0;
     authserver=NULL;
+    request = NULL;
 }
 
 AuthNTLMUserRequest::~AuthNTLMUserRequest()
@@ -727,10 +721,14 @@ AuthNTLMUserRequest::~AuthNTLMUserRequest()
     safe_free(client_blob);
 
     if (authserver != NULL) {
-        debug(29, 9) ("AuthNTLMUserRequest::~AuthNTLMUserRequest: releasing server '%p'\n", authserver);
+        debugs(29, 9, "AuthNTLMUserRequest::~AuthNTLMUserRequest: releasing server '" << authserver << "'");
         helperStatefulReleaseServer(authserver);
         authserver = NULL;
     }
+    if (request) {
+        HTTPMSGUNLOCK(request);
+        request = NULL;
+    }
 }
 
 void