---------------------
PatchSet 6380 
Date: 2005/01/12 23:08:57
Author: squidadm
Branch: negotiate-2_5
Tag: (none) 
Log:
Initial import

Members: 
	configure.in:1.42.2.51->1.42.2.51.2.1 
	src/authenticate.c:1.23.2.13->1.23.2.13.2.1 
	src/cf.data.pre:1.49.2.65->1.49.2.65.2.1 
	src/enums.h:1.29.2.13->1.29.2.13.2.1 
	src/auth/Makefile.am:1.2->1.2.94.1 
	src/auth/negotiate/Makefile.am:1.1->1.1.2.1 
	src/auth/negotiate/auth_negotiate.c:1.1->1.1.2.1 
	src/auth/negotiate/auth_negotiate.h:1.1->1.1.2.1 
	src/auth/ntlm/auth_ntlm.c:1.18.2.14->1.18.2.14.2.1 
	src/auth/ntlm/auth_ntlm.h:1.8.2.4->1.8.2.4.2.1 

Index: squid/configure.in
===================================================================
RCS file: /cvsroot/squid-sf//squid/configure.in,v
retrieving revision 1.42.2.51
retrieving revision 1.42.2.51.2.1
diff -u -r1.42.2.51 -r1.42.2.51.2.1
--- squid/configure.in	12 Oct 2004 02:13:34 -0000	1.42.2.51
+++ squid/configure.in	12 Jan 2005 23:08:57 -0000	1.42.2.51.2.1
@@ -3,7 +3,7 @@
 dnl
 dnl  Duane Wessels, wessels@nlanr.net, February 1996 (autoconf v2.9)
 dnl
-dnl  $Id: configure.in,v 1.42.2.51 2004/10/12 02:13:34 squidadm Exp $
+dnl  $Id: configure.in,v 1.42.2.51.2.1 2005/01/12 23:08:57 squidadm Exp $
 dnl
 dnl
 dnl
@@ -11,7 +11,7 @@
 AC_CONFIG_AUX_DIR(cfgaux)
 AM_INIT_AUTOMAKE(squid, 2.5.STABLE7-CVS)
 AM_CONFIG_HEADER(include/autoconf.h)
-AC_REVISION($Revision: 1.42.2.51 $)dnl
+AC_REVISION($Revision: 1.42.2.51.2.1 $)dnl
 AC_PREFIX_DEFAULT(/usr/local/squid)
 AM_MAINTAINER_MODE
 
@@ -2342,6 +2342,7 @@
 	src/auth/basic/Makefile \
 	src/auth/digest/Makefile \
 	src/auth/ntlm/Makefile \
+	src/auth/negotiate/Makefile \
 	contrib/Makefile \
 	snmplib/Makefile \
 	icons/Makefile \
Index: squid/src/authenticate.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/authenticate.c,v
retrieving revision 1.23.2.13
retrieving revision 1.23.2.13.2.1
diff -u -r1.23.2.13 -r1.23.2.13.2.1
--- squid/src/authenticate.c	11 Jan 2005 03:17:25 -0000	1.23.2.13
+++ squid/src/authenticate.c	12 Jan 2005 23:09:00 -0000	1.23.2.13.2.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: authenticate.c,v 1.23.2.13 2005/01/11 03:17:25 squidadm Exp $
+ * $Id: authenticate.c,v 1.23.2.13.2.1 2005/01/12 23:09:00 squidadm Exp $
  *
  * DEBUG: section 29    Authenticator
  * AUTHOR: Duane Wessels
@@ -753,11 +753,16 @@
 	    /* call each configured & running authscheme */
 	    for (i = 0; i < Config.authConfig.n_configured; i++) {
 		scheme = Config.authConfig.schemes + i;
-		if (authscheme_list[scheme->Id].Active())
-		    authscheme_list[scheme->Id].authFixHeader(NULL, rep, type,
-			request);
-		else
+		if (authscheme_list[scheme->Id].Active()) {
+		    if  (auth_user_request && scheme->Id == auth_user_request->auth_user->auth_module - 1)
+			authscheme_list[scheme->Id].authFixHeader(
+				auth_user_request, rep, type, request);
+		    else
+			authscheme_list[scheme->Id].authFixHeader(
+				NULL, rep, type, request);
+		} else {
 		    debug(29, 4) ("authenticateFixHeader: Configured scheme %s not Active\n", scheme->typestr);
+		}
 	    }
 	}
     }
Index: squid/src/cf.data.pre
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/cf.data.pre,v
retrieving revision 1.49.2.65
retrieving revision 1.49.2.65.2.1
diff -u -r1.49.2.65 -r1.49.2.65.2.1
--- squid/src/cf.data.pre	6 Jan 2005 03:14:30 -0000	1.49.2.65
+++ squid/src/cf.data.pre	12 Jan 2005 23:09:01 -0000	1.49.2.65.2.1
@@ -1,6 +1,6 @@
 
 #
-# $Id: cf.data.pre,v 1.49.2.65 2005/01/06 03:14:30 squidadm Exp $
+# $Id: cf.data.pre,v 1.49.2.65.2.1 2005/01/12 23:09:01 squidadm Exp $
 #
 #
 # SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -1441,20 +1441,6 @@
 	lots of authenticator 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 (the default) means use the
-	challenge is used only once. See also the max_ntlm_challenge_lifetime
-	directive if enabling challenge reuses.
-	auth_param ntlm max_challenge_reuses 0
-
-	"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
-
 	"use_ntlm_negotiate" on|off
 	Enables support for NTLM NEGOTIATE packet exchanges with the helper.
 	The configured ntlm authenticator must be able to handle NTLM
@@ -1474,6 +1460,37 @@
 	max_challenge_lifetime parameters and set them to 0.
 	auth_param ntlm use_ntlm_negotiate off
 
+	"keep_alive" on|off
+	This option enables the use of keep-alive on the initial
+	authentication request. It has been reported some versions of MSIE
+	have problems if this is enabled, but performance will be increased
+	if enabled.
+
+	auth_param ntlm keep_alive off
+
+	=== Negotiate scheme options follow ===
+
+	"program" cmdline
+	Specify the command for the external SPNEGO authenticator. Such a
+	program participates in the SPNEGO exchanges between Squid and the
+	client and reads commands according to the Squid ntlmssp helper
+	protocol. See helpers/ntlm_auth/ for details. Recommended SPNEGO
+	authenticator is ntlm_auth from Samba-3.X.
+
+	By default, the Negotiate authentication scheme is not used unless a
+	program is specified.
+
+	auth_param negotiate program /path/to/samba/bin/ntlm_auth --helper-protocol=gss-spnego
+
+	"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 digest program <uncomment and complete this line>
Index: squid/src/enums.h
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/enums.h,v
retrieving revision 1.29.2.13
retrieving revision 1.29.2.13.2.1
diff -u -r1.29.2.13 -r1.29.2.13.2.1
--- squid/src/enums.h	6 Oct 2004 02:15:16 -0000	1.29.2.13
+++ squid/src/enums.h	12 Jan 2005 23:09:04 -0000	1.29.2.13.2.1
@@ -1,6 +1,6 @@
 
 /*
- * $Id: enums.h,v 1.29.2.13 2004/10/06 02:15:16 squidadm Exp $
+ * $Id: enums.h,v 1.29.2.13.2.1 2005/01/12 23:09:04 squidadm Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -530,6 +530,7 @@
     AUTH_BASIC,
     AUTH_NTLM,
     AUTH_DIGEST,
+    AUTH_NEGOTIATE,
     AUTH_BROKEN			/* known type, but broken data */
 } auth_type_t;
 
Index: squid/src/auth/Makefile.am
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/auth/Makefile.am,v
retrieving revision 1.2
retrieving revision 1.2.94.1
diff -u -r1.2 -r1.2.94.1
--- squid/src/auth/Makefile.am	1 Sep 2001 11:46:49 -0000	1.2
+++ squid/src/auth/Makefile.am	12 Jan 2005 23:09:04 -0000	1.2.94.1
@@ -4,15 +4,16 @@
 #
 AUTOMAKE_OPTIONS = subdir-objects
 
-DIST_SUBDIRS	= basic digest ntlm
+DIST_SUBDIRS	= basic digest ntlm negotiate
 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.c basic/auth_basic.h
 libdigest_a_SOURCES	= digest/auth_digest.c digest/auth_digest.h
 libntlm_a_SOURCES	= ntlm/auth_ntlm.c ntlm/auth_ntlm.h
+libnegotiate_a_SOURCES	= negotiate/auth_negotiate.c negotiate/auth_negotiate.h
 
 INCLUDES      = -I. -I$(top_builddir)/include -I$(top_srcdir)/include \
 	-I$(top_srcdir)/src/
--- /dev/null	Wed Feb 14 01:10:31 2007
+++ squid/src/auth/negotiate/Makefile.am	Wed Feb 14 01:10:32 2007
@@ -0,0 +1 @@
+SUBDIRS =
--- /dev/null	Wed Feb 14 01:10:31 2007
+++ squid/src/auth/negotiate/auth_negotiate.c	Wed Feb 14 01:10:32 2007
@@ -0,0 +1,753 @@
+
+/*
+ * $Id: auth_negotiate.c,v 1.1.2.1 2005/01/12 23:09:05 squidadm Exp $
+ *
+ * DEBUG: section 29    Negotiate Authenticator
+ * AUTHOR: Robert Collins
+ *
+ * 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"
+
+extern AUTHSSETUP authSchemeSetup_negotiate;
+
+static void
+authenticateStateFree(authenticateStateData * r)
+{
+    authenticateAuthUserRequestUnlock(r->auth_user_request);
+    r->auth_user_request = NULL;
+    cbdataFree(r);
+}
+
+/* Negotiate Scheme */
+static HLPSCB authenticateNegotiateHandleReply;
+static AUTHSACTIVE authenticateNegotiateActive;
+static AUTHSAUTHED authNegotiateAuthenticated;
+static AUTHSAUTHUSER authenticateNegotiateAuthenticateUser;
+static AUTHSCONFIGURED authNegotiateConfigured;
+static AUTHSFIXERR authenticateNegotiateFixErrorHeader;
+static AUTHSADDHEADER authNegotiateAddHeader;
+static AUTHSFREE authenticateNegotiateFreeUser;
+static AUTHSDIRECTION authenticateNegotiateDirection;
+static AUTHSDECODE authenticateDecodeNegotiateAuth;
+static AUTHSDUMP authNegotiateCfgDump;
+static AUTHSFREECONFIG authNegotiateFreeConfig;
+static AUTHSINIT authNegotiateInit;
+static AUTHSONCLOSEC authenticateNegotiateOnCloseConnection;
+static AUTHSUSERNAME authenticateNegotiateUsername;
+static AUTHSREQFREE authNegotiateAURequestFree;
+static AUTHSPARSE authNegotiateParse;
+static AUTHSSTART authenticateNegotiateStart;
+static AUTHSSTATS authenticateNegotiateStats;
+static AUTHSSHUTDOWN authNegotiateDone;
+
+static statefulhelper *negotiateauthenticators = NULL;
+
+CBDATA_TYPE(authenticateStateData);
+
+static int authnegotiate_initialised = 0;
+
+static MemPool *negotiate_user_pool = NULL;
+static MemPool *negotiate_request_pool = NULL;
+static auth_negotiate_config *negotiateConfig = NULL;
+
+static void authenticateNegotiateReleaseServer(negotiate_request_t * negotiate_request);
+/*
+ *
+ * Private Functions
+ *
+ */
+
+static void
+authNegotiateDone(void)
+{
+    debug(29, 2) ("authNegotiateDone: shutting down Negotiate authentication.\n");
+    if (negotiateauthenticators)
+	helperStatefulShutdown(negotiateauthenticators);
+    authnegotiate_initialised = 0;
+    if (!shutting_down)
+	return;
+    if (negotiateauthenticators)
+	helperStatefulFree(negotiateauthenticators);
+    negotiateauthenticators = NULL;
+    if (negotiate_request_pool) {
+	assert(memPoolInUseCount(negotiate_request_pool) == 0);
+	memPoolDestroy(negotiate_request_pool);
+	negotiate_request_pool = NULL;
+    }
+    if (negotiate_user_pool) {
+	assert(memPoolInUseCount(negotiate_user_pool) == 0);
+	memPoolDestroy(negotiate_user_pool);
+	negotiate_user_pool = NULL;
+    }
+    debug(29, 2) ("authNegotiateDone: Negotiate authentication Shutdown.\n");
+}
+
+/* free any allocated configuration details */
+static void
+authNegotiateFreeConfig(authScheme * scheme)
+{
+    if (negotiateConfig == NULL)
+	return;
+    assert(negotiateConfig == scheme->scheme_data);
+    if (negotiateConfig->authenticate)
+	wordlistDestroy(&negotiateConfig->authenticate);
+    xfree(negotiateConfig);
+    negotiateConfig = NULL;
+}
+
+static void
+authNegotiateCfgDump(StoreEntry * entry, const char *name, authScheme * scheme)
+{
+    auth_negotiate_config *config = scheme->scheme_data;
+    wordlist *list = config->authenticate;
+    storeAppendPrintf(entry, "%s %s", name, "negotiate");
+    while (list != NULL) {
+	storeAppendPrintf(entry, " %s", list->key);
+	list = list->next;
+    }
+    storeAppendPrintf(entry, "\n%s %s children %d\n", name, "negotiate", config->authenticateChildren);
+    storeAppendPrintf(entry, "\n%s %s keep_alive %d\n", name, "negotiate", config->keep_alive);
+}
+
+static void
+authNegotiateParse(authScheme * scheme, int n_configured, char *param_str)
+{
+    if (scheme->scheme_data == NULL) {
+	assert(negotiateConfig == NULL);
+	/* this is the first param to be found */
+	scheme->scheme_data = xmalloc(sizeof(auth_negotiate_config));
+	memset(scheme->scheme_data, 0, sizeof(auth_negotiate_config));
+	negotiateConfig = scheme->scheme_data;
+	negotiateConfig->authenticateChildren = 5;
+	negotiateConfig->keep_alive = 1;
+    }
+    negotiateConfig = scheme->scheme_data;
+    if (strcasecmp(param_str, "program") == 0) {
+	if (negotiateConfig->authenticate)
+	    wordlistDestroy(&negotiateConfig->authenticate);
+	parse_wordlist(&negotiateConfig->authenticate);
+	requirePathnameExists("authparam negotiate program", negotiateConfig->authenticate->key);
+    } else if (strcasecmp(param_str, "children") == 0) {
+	parse_int(&negotiateConfig->authenticateChildren);
+    } else if (strcasecmp(param_str, "keep_alive") == 0) {
+	parse_onoff(&negotiateConfig->keep_alive);
+    } else {
+	debug(28, 0) ("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 (negotiateConfig->authenticate && Config.onoff.pipeline_prefetch != 0)
+	Config.onoff.pipeline_prefetch = 0;
+}
+
+
+void
+authSchemeSetup_negotiate(authscheme_entry_t * authscheme)
+{
+    assert(!authnegotiate_initialised);
+    authscheme->Active = authenticateNegotiateActive;
+    authscheme->configured = authNegotiateConfigured;
+    authscheme->parse = authNegotiateParse;
+    authscheme->dump = authNegotiateCfgDump;
+    authscheme->requestFree = authNegotiateAURequestFree;
+    authscheme->freeconfig = authNegotiateFreeConfig;
+    authscheme->init = authNegotiateInit;
+    authscheme->authAuthenticate = authenticateNegotiateAuthenticateUser;
+    authscheme->authenticated = authNegotiateAuthenticated;
+    authscheme->authFixHeader = authenticateNegotiateFixErrorHeader;
+    authscheme->AddHeader = authNegotiateAddHeader;
+    authscheme->FreeUser = authenticateNegotiateFreeUser;
+    authscheme->authStart = authenticateNegotiateStart;
+    authscheme->authStats = authenticateNegotiateStats;
+    authscheme->authUserUsername = authenticateNegotiateUsername;
+    authscheme->getdirection = authenticateNegotiateDirection;
+    authscheme->decodeauth = authenticateDecodeNegotiateAuth;
+    authscheme->donefunc = authNegotiateDone;
+    authscheme->oncloseconnection = authenticateNegotiateOnCloseConnection;
+}
+
+/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
+ * config file */
+static void
+authNegotiateInit(authScheme * scheme)
+{
+    static int negotiateinit = 0;
+    if (negotiateConfig->authenticate) {
+	if (!negotiate_user_pool)
+	    negotiate_user_pool = memPoolCreate("Negotiate Scheme User Data", sizeof(negotiate_user_t));
+	if (!negotiate_request_pool)
+	    negotiate_request_pool = memPoolCreate("Negotiate Scheme Request Data", sizeof(negotiate_request_t));
+	authnegotiate_initialised = 1;
+	if (negotiateauthenticators == NULL)
+	    negotiateauthenticators = helperStatefulCreate("negotiateauthenticator");
+	negotiateauthenticators->cmdline = negotiateConfig->authenticate;
+	negotiateauthenticators->n_to_start = negotiateConfig->authenticateChildren;
+	negotiateauthenticators->ipc_type = IPC_TCP_SOCKET;
+	helperStatefulOpenServers(negotiateauthenticators);
+	if (!negotiateinit) {
+	    cachemgrRegister("negotiateauthenticator",
+		"Negotiate User Authenticator Stats",
+		authenticateNegotiateStats, 0, 1);
+	    negotiateinit++;
+	}
+	CBDATA_INIT_TYPE(authenticateStateData);
+    }
+}
+
+static int
+authenticateNegotiateActive()
+{
+    return (authnegotiate_initialised == 1) ? 1 : 0;
+}
+
+
+static int
+authNegotiateConfigured()
+{
+    if (negotiateConfig == NULL) {
+	debug(29, 9) ("authNegotiateConfigured: not configured\n");
+	return 0;
+    }
+    if (negotiateConfig->authenticate == NULL) {
+	debug(29, 9) ("authNegotiateConfigured: no helper\n");
+	return 0;
+    }
+    if (negotiateConfig->authenticateChildren == 0) {
+	debug(29, 9) ("authNegotiateConfigured: no helper children\n");
+	return 0;
+    }
+    debug(29, 9) ("authNegotiateConfigured: returning configured\n");
+    return 1;
+}
+
+/* Negotiate Scheme */
+
+static int
+authenticateNegotiateDirection(auth_user_request_t * auth_user_request)
+{
+    negotiate_request_t *negotiate_request = auth_user_request->scheme_data;
+    /* null auth_user is checked for by authenticateDirection */
+    if (negotiate_request->waiting || negotiate_request->client_blob)
+	return -1;			/* Need helper response to continue */
+    switch (negotiate_request->auth_state) {
+    case AUTHENTICATE_STATE_NONE:	/* no progress at all. */
+	debug(29, 1) ("authenticateNegotiateDirection: called before Negotiate Authenticate!. Report a bug to squid-dev. au %p\n", auth_user_request);
+	return 1;
+    case AUTHENTICATE_STATE_NEGOTIATE:	/* send to client */
+	assert(negotiate_request->server_blob);
+	return 1;
+    case AUTHENTICATE_STATE_FAILED:
+	return -2;
+    case AUTHENTICATE_STATE_FINISHED:	/* do nothing.. */
+    case AUTHENTICATE_STATE_DONE:	/* do nothing.. */
+	return 0;
+    case AUTHENTICATE_STATE_INITIAL:
+	debug(29, 1) ("authenticateNegotiateDirection: Unexpected AUTHENTICATE_STATE_INITIAL\n");
+	return -2;
+    }
+    return -2;
+}
+
+/*
+ * Send the authenticate error header(s). Note: IE has a bug and the Negotiate header
+ * must be first. To ensure that, the configure use --enable-auth=negotiate, anything
+ * else.
+ */
+static void
+authenticateNegotiateFixErrorHeader(auth_user_request_t * auth_user_request, HttpReply * rep, http_hdr_type type, request_t * request)
+{
+    negotiate_request_t *negotiate_request;
+    if (!negotiateConfig->authenticate)
+	return;
+    /* New request, no user details */
+    if (auth_user_request == NULL) {
+	debug(29, 9) ("authenticateNegotiateFixErrorHeader: Sending type:%d header: 'Negotiate'\n", type);
+	httpHeaderPutStrf(&rep->header, type, "Negotiate");
+	if (!negotiateConfig->keep_alive) {
+	    /* drop the connection */
+	    httpHeaderDelByName(&rep->header, "keep-alive");
+	    request->flags.proxy_keepalive = 0;
+	}
+	return;
+    }
+    negotiate_request = auth_user_request->scheme_data;
+    switch (negotiate_request->auth_state) {
+    case AUTHENTICATE_STATE_NONE:
+    case AUTHENTICATE_STATE_FAILED:
+	debug(29, 9) ("authenticateNegotiateFixErrorHeader: Sending type:%d header: 'Negotiate'\n", type);
+	httpHeaderPutStrf(&rep->header, type, "Negotiate");
+	/* drop the connection */
+	httpHeaderDelByName(&rep->header, "keep-alive");
+	request->flags.proxy_keepalive = 0;
+	break;
+    case AUTHENTICATE_STATE_NEGOTIATE:
+	/* we are 'waiting' for a response from the client */
+	/* pass the blob to the client */
+	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);
+	break;
+    case AUTHENTICATE_STATE_FINISHED:
+	/* Special case when authentication finished, but not allowed by ACL */
+	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");
+	}
+	break;
+    default:
+	debug(29, 0) ("authenticateNegotiateFixErrorHeader: state %d.\n", negotiate_request->auth_state);
+	fatal("unexpected state in AuthenticateNegotiateFixErrorHeader.\n");
+    }
+}
+
+/* add the [proxy]authorisation header */
+static void
+authNegotiateAddHeader(auth_user_request_t * auth_user_request, HttpReply * rep, int accel)
+{
+    int type;
+    negotiate_request_t *negotiate_request;
+    if (!auth_user_request)
+	return;
+    negotiate_request = auth_user_request->scheme_data;
+    if (!negotiate_request->server_blob)
+	return;
+
+    type = accel ? HDR_WWW_AUTHENTICATE : HDR_PROXY_AUTHENTICATE;
+
+    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);
+}
+
+static void
+authNegotiateRequestFree(negotiate_request_t * negotiate_request)
+{
+    if (!negotiate_request)
+	return;
+    safe_free(negotiate_request->server_blob);
+    if (negotiate_request->authserver != NULL) {
+	debug(29, 9) ("authenticateNegotiateRequestFree: releasing server '%p'\n", negotiate_request->authserver);
+	authenticateNegotiateReleaseServer(negotiate_request);
+    }
+    memPoolFree(negotiate_request_pool, negotiate_request);
+}
+
+static void
+authNegotiateAURequestFree(auth_user_request_t * auth_user_request)
+{
+    if (auth_user_request->scheme_data)
+	authNegotiateRequestFree((negotiate_request_t *) auth_user_request->scheme_data);
+    auth_user_request->scheme_data = NULL;
+}
+
+static void
+authenticateNegotiateFreeUser(auth_user_t * auth_user)
+{
+    negotiate_user_t *negotiate_user = auth_user->scheme_data;
+
+    debug(29, 5) ("authenticateNegotiateFreeUser: Clearing Negotiate scheme data\n");
+    if (negotiate_user->username)
+	xfree(negotiate_user->username);
+    memPoolFree(negotiate_user_pool, negotiate_user);
+    auth_user->scheme_data = NULL;
+}
+
+/* clear the Negotiate helper of being reserved for future requests */
+static void
+authenticateNegotiateReleaseServer(negotiate_request_t * negotiate_request)
+{
+    helper_stateful_server *server = negotiate_request->authserver;
+    if (!server)
+	return;
+    debug(29, 9) ("authenticateNegotiateReleaseServer: releasing server '%p'\n", server);
+    negotiate_request->authserver = NULL;
+    helperStatefulReleaseServer(server);
+}
+
+static void
+authenticateNegotiateHandleReply(void *data, void *srv, char *reply)
+{
+    authenticateStateData *r = data;
+    int valid;
+    auth_user_request_t *auth_user_request;
+    auth_user_t *auth_user;
+    negotiate_user_t *negotiate_user;
+    negotiate_request_t *negotiate_request;
+    char *blob, *arg = NULL;
+    debug(29, 9) ("authenticateNegotiateHandleReply: Helper: '%p' {%s}\n", srv, reply ? reply : "<NULL>");
+    valid = cbdataValid(r->data);
+    if (!valid) {
+	debug(29, 2) ("AuthenticateNegotiateHandleReply: invalid callback data. Releasing helper '%p'.\n", srv);
+	cbdataUnlock(r->data);
+	authenticateStateFree(r);
+	return;
+    }
+    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;
+
+    negotiate_request = auth_user_request->scheme_data;
+    assert(negotiate_request != NULL);
+
+    assert(negotiate_request->waiting);
+    negotiate_request->waiting = 0;
+    safe_free(negotiate_request->client_blob);
+
+    auth_user = auth_user_request->auth_user;
+    assert(auth_user != NULL);
+    assert(auth_user->auth_type == AUTH_NEGOTIATE);
+    negotiate_user = auth_user_request->auth_user->scheme_data;
+
+    if (negotiate_request->authserver == NULL)
+	negotiate_request->authserver = srv;
+    else
+	assert(negotiate_request->authserver == srv);
+
+    blob = strchr(reply, ' ');
+    if (blob) {
+	blob++;
+	arg = strchr(blob + 1, ' ');
+    }
+    
+    /* seperate out the useful data */
+    if (strncasecmp(reply, "TT ", 3) == 0 && blob != NULL) {
+	/* we have been given a blob to send to the client */
+	if (arg)
+	    *arg++ = '\0';
+	negotiate_request->authserver = srv;
+	safe_free(negotiate_request->server_blob);
+	negotiate_request->server_blob = xstrdup(blob);
+	negotiate_request->auth_state = AUTHENTICATE_STATE_NEGOTIATE;
+	safe_free(auth_user_request->message);
+	auth_user_request->message = xstrdup("Authenication in progress");
+	debug(29, 4) ("authenticateNegotiateHandleReply: Need to challenge the client with a server blob '%s'\n", blob);
+    } else if (strncasecmp(reply, "AF ", 3) == 0 && arg != NULL) {
+	/* we're finished, release the helper */
+	if (arg)
+	    *arg++ = '\0';
+	safe_free(negotiate_user->username);
+	negotiate_user->username = xstrdup(arg);
+	safe_free(auth_user_request->message);
+	auth_user_request->message = xstrdup("Login successful");
+	safe_free(negotiate_request->server_blob);
+	negotiate_request->server_blob = xstrdup(blob);
+	debug(29, 4) ("authenticateNegotiateHandleReply: Successfully validated user via Negotiate. Username '%s'\n", arg);
+	authenticateNegotiateReleaseServer(negotiate_request);
+	negotiate_request->auth_state = AUTHENTICATE_STATE_FINISHED;
+    } else if (strncasecmp(reply, "NA ", 3) == 0 && arg != NULL) {
+	if (arg)
+	    *arg++ = '\0';
+	safe_free(auth_user_request->message);
+	auth_user_request->message = xstrdup(arg);
+	negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
+	safe_free(negotiate_request->server_blob);
+	negotiate_request->server_blob = xstrdup(blob);
+	authenticateNegotiateReleaseServer(negotiate_request);
+	debug(29, 4) ("authenticateNegotiateHandleReply: Failed validating user via Negotiate. Error returned '%s'\n", arg);
+    } 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->message = xstrdup(reply);
+	negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
+	safe_free(negotiate_request->server_blob);
+	authenticateNegotiateReleaseServer(negotiate_request);
+	debug(29, 1) ("authenticateNegotiateHandleReply: Error validating user via Negotiate. Error returned '%s'\n", reply);
+    } else {
+	fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
+    }
+    r->handler(r->data, NULL);
+    cbdataUnlock(r->data);
+    authenticateStateFree(r);
+}
+
+static void
+authenticateNegotiateStats(StoreEntry * sentry)
+{
+    storeAppendPrintf(sentry, "Negotiate Authenticator Statistics:\n");
+    helperStatefulStats(sentry, negotiateauthenticators);
+}
+
+/* send the initial data to a stateful negotiate authenticator module */
+static void
+authenticateNegotiateStart(auth_user_request_t * auth_user_request, RH * handler, void *data)
+{
+    authenticateStateData *r = NULL;
+    char buf[8192];
+    char *sent_string = NULL;
+    negotiate_user_t *negotiate_user;
+    negotiate_request_t *negotiate_request;
+    auth_user_t *auth_user;
+
+    assert(auth_user_request);
+    auth_user = auth_user_request->auth_user;
+    negotiate_user = auth_user->scheme_data;
+    negotiate_request = auth_user_request->scheme_data;
+    assert(negotiate_user);
+    assert(negotiate_request);
+    assert(handler);
+    assert(data);
+    assert(auth_user->auth_type == AUTH_NEGOTIATE);
+    debug(29, 9) ("authenticateNegotiateStart: auth state '%d'\n", negotiate_request->auth_state);
+    sent_string = negotiate_request->client_blob;
+
+    debug(29, 9) ("authenticateNegotiateStart: state '%d'\n", negotiate_request->auth_state);
+    debug(29, 9) ("authenticateNegotiateStart: '%s'\n", sent_string);
+    if (negotiateConfig->authenticate == NULL) {
+	debug(29, 0) ("authenticateNegotiateStart: no Negotiate program specified:'%s'\n", sent_string);
+	handler(data, NULL);
+	return;
+    }
+
+    /* Send blob to helper */
+    r = cbdataAlloc(authenticateStateData);
+    r->handler = handler;
+    cbdataLock(data);
+    r->data = data;
+    r->auth_user_request = auth_user_request;
+    authenticateAuthUserRequestLock(r->auth_user_request);
+    if (negotiate_request->auth_state == AUTHENTICATE_STATE_INITIAL)
+	snprintf(buf, 8192, "YR %s\n", sent_string);
+    else
+	snprintf(buf, 8192, "KK %s\n", sent_string);
+    negotiate_request->waiting = 1;
+    safe_free(negotiate_request->client_blob);
+    helperStatefulSubmit(negotiateauthenticators, buf, authenticateNegotiateHandleReply, r, negotiate_request->authserver);
+}
+
+/* clear any connection related authentication details */
+static void
+authenticateNegotiateOnCloseConnection(ConnStateData * conn)
+{
+    negotiate_request_t *negotiate_request;
+    assert(conn != NULL);
+    if (conn->auth_user_request != NULL) {
+	assert(conn->auth_user_request->scheme_data != NULL);
+	negotiate_request = conn->auth_user_request->scheme_data;
+	assert(negotiate_request->conn == conn);
+	if (negotiate_request->authserver != NULL)
+	    authenticateNegotiateReleaseServer(negotiate_request);
+	/* unlock the connection based lock */
+	debug(29, 9) ("authenticateNegotiateOnCloseConnection: Unlocking auth_user from the connection.\n");
+	/* minor abstraction break here: FIXME */
+	/* Ensure that the auth user request will be getting closed */
+	/* IFF we start persisting the struct after the conn closes - say for logging
+	 * then this test may become invalid
+	 */
+	assert(conn->auth_user_request->references == 1);
+	authenticateAuthUserRequestUnlock(conn->auth_user_request);
+	conn->auth_user_request = NULL;
+    }
+}
+
+/* authenticateUserUsername: return a pointer to the username in the */
+static char *
+authenticateNegotiateUsername(auth_user_t * auth_user)
+{
+    negotiate_user_t *negotiate_user = auth_user->scheme_data;
+    if (negotiate_user)
+	return negotiate_user->username;
+    return NULL;
+}
+
+/*
+ * Called on the initial request only, to set things up for later processing
+ */
+
+static void
+authenticateDecodeNegotiateAuth(auth_user_request_t * auth_user_request, const char *proxy_auth)
+{
+    dlink_node *node;
+    assert(auth_user_request->auth_user == NULL);
+    auth_user_request->auth_user = authenticateAuthUserNew("negotiate");
+    auth_user_request->auth_user->auth_type = AUTH_NEGOTIATE;
+    auth_user_request->auth_user->scheme_data = memPoolAlloc(negotiate_user_pool);
+    auth_user_request->scheme_data = memPoolAlloc(negotiate_request_pool);
+    memset(auth_user_request->scheme_data, '\0', sizeof(negotiate_request_t));
+    /* lock for the auth_user_request link */
+    authenticateAuthUserLock(auth_user_request->auth_user);
+    node = dlinkNodeNew();
+    dlinkAdd(auth_user_request, node, &auth_user_request->auth_user->requests);
+
+    /* the helper does the rest, with data collected in
+     * authenticateNegotiateAuthenticateUser */
+    debug(29, 9) ("authenticateDecodeNegotiateAuth: Negotiate authentication\n");
+    return;
+}
+
+static int
+authenticateNegotiatecmpUsername(negotiate_user_t * u1, negotiate_user_t * u2)
+{
+    return strcmp(u1->username, u2->username);
+}
+
+
+static int
+authNegotiateAuthenticated(auth_user_request_t * auth_user_request)
+{
+    negotiate_request_t *negotiate_request = auth_user_request->scheme_data;
+    if (negotiate_request->auth_state == AUTHENTICATE_STATE_FINISHED)
+	return 1;
+    debug(29, 9) ("User not fully authenticated.\n");
+    return 0;
+}
+
+static void
+authenticateNegotiateAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type)
+{
+    const char *proxy_auth, *blob;
+    auth_user_hash_pointer *usernamehash;
+    auth_user_t *auth_user;
+    negotiate_request_t *negotiate_request;
+    negotiate_user_t *negotiate_user;
+
+    auth_user = auth_user_request->auth_user;
+    assert(auth_user);
+    assert(auth_user->auth_type == AUTH_NEGOTIATE);
+    assert(auth_user->scheme_data != NULL);
+    assert(auth_user_request->scheme_data != NULL);
+    negotiate_user = auth_user->scheme_data;
+    negotiate_request = auth_user_request->scheme_data;
+    /* Check that we are in the client side, where we can generate
+     * auth challenges */
+    if (!conn) {
+	negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
+	debug(29, 1) ("authenticateNegotiateAuthenticateUser: attempt to perform authentication without a connection!\n");
+	return;
+    }
+    if (negotiate_request->waiting) {
+	debug(29, 1) ("authenticateNegotiateAuthenticateUser: waiting for helper reply!\n");
+	return;
+    }
+    if (negotiate_request->server_blob) {
+	debug(29, 2) ("authenticateNegotiateAuthenticateUser: need to challenge client '%s'!\n", negotiate_request->server_blob);
+	return;
+    }
+
+    /* get header */
+    proxy_auth = httpHeaderGetStr(&request->header, type);
+    if (strncasecmp("Negotiate ", proxy_auth, 10) != 0) {
+	fatal("Incorrect scheme in auth header\n");
+	/* TODO: more fault tolerance.. reset the auth scheme here */
+    }
+    blob = proxy_auth + 10;
+    while (xisspace(*blob))      /*trim leading spaces */
+	blob++;
+
+    switch (negotiate_request->auth_state) {
+    case AUTHENTICATE_STATE_NONE:
+	/* we've recieved a negotiate request. pass to a helper */
+	debug(29, 9) ("authenticateNegotiateAuthenticateUser: auth state negotiate none. %s\n", proxy_auth);
+	negotiate_request->auth_state = AUTHENTICATE_STATE_INITIAL;
+	safe_free(negotiate_request->client_blob);
+	negotiate_request->client_blob = xstrdup(blob);
+	conn->auth_type = AUTH_NEGOTIATE;
+	conn->auth_user_request = auth_user_request;
+	negotiate_request->conn = conn;
+	/* and lock for the connection duration */
+	debug(29, 9) ("authenticateNegotiateAuthenticateUser: Locking auth_user from the connection.\n");
+	authenticateAuthUserRequestLock(auth_user_request);
+	return;
+	break;
+    case AUTHENTICATE_STATE_INITIAL:
+	debug(29, 1) ("authenticateNegotiateAuthenticateUser: need to ask helper!\n");
+	return;
+	break;
+    case AUTHENTICATE_STATE_NEGOTIATE:
+	/* we should have recieved a blob from the clien. pass it to the same 
+	 * helper process */
+	debug(29, 9) ("authenticateNegotiateAuthenticateUser: auth state challenge with header %s.\n", proxy_auth);
+	/* do a cache lookup here. If it matches it's a successful negotiate 
+	 * challenge - release the helper and use the existing auth_user 
+	 * details. */
+	safe_free(negotiate_request->client_blob);
+	negotiate_request->client_blob = xstrdup(blob);
+	return;
+	break;
+    case AUTHENTICATE_STATE_FINISHED:
+	/* this connection is authenticated */
+	debug(29, 4) ("authenticated user %s\n", negotiate_user->username);
+	/* see if this is an existing user with a different proxy_auth 
+	 * string */
+	usernamehash = hash_lookup(proxy_auth_username_cache, negotiate_user->username);
+	if (usernamehash) {
+	    while (usernamehash && (usernamehash->auth_user->auth_type != auth_user->auth_type || authenticateNegotiatecmpUsername(usernamehash->auth_user->scheme_data, negotiate_user) != 0))
+		usernamehash = usernamehash->next;
+	}
+	if (usernamehash) {
+	    /* 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->auth_user);
+	    auth_user = usernamehash->auth_user;
+	    auth_user_request->auth_user = auth_user;
+	} else {
+	    /* store user in hash's */
+	    authenticateUserNameCacheAdd(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;
+	authenticateNegotiateReleaseServer(negotiate_request);
+	negotiate_request->auth_state = AUTHENTICATE_STATE_DONE;
+	return;
+    case AUTHENTICATE_STATE_DONE:
+	fatal("authenticateNegotiateAuthenticateUser: 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) ("authenticateNegotiateAuthenticateUser: auth state negotiate failed. %s\n", proxy_auth);
+	return;
+    }
+    return;
+}
--- /dev/null	Wed Feb 14 01:10:31 2007
+++ squid/src/auth/negotiate/auth_negotiate.h	Wed Feb 14 01:10:32 2007
@@ -0,0 +1,62 @@
+/*
+ * auth_negotiate.h
+ * Internal declarations for the negotiate auth module
+ */
+
+#ifndef __AUTH_Negotiate_H__
+#define __AUTH_Negotiate_H__
+
+#define DefaultAuthenticateChildrenMax  32	/* 32 processes */
+
+typedef enum {
+    AUTHENTICATE_STATE_NONE,
+    AUTHENTICATE_STATE_INITIAL,
+    AUTHENTICATE_STATE_NEGOTIATE,
+    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;
+
+struct _negotiate_user {
+    /* what username did this connection get? */
+    char *username;
+};
+
+struct _negotiate_request {
+    /*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;
+    /* currently waiting for helper response */
+    int waiting;
+    /* what connection is this associated with */
+    ConnStateData *conn;
+    /* our current blob to pass to the client */
+    char *server_blob;
+    /* our current blob to pass to the server */
+    char *client_blob;
+};
+
+/* configuration runtime data */
+struct _auth_negotiate_config {
+    int authenticateChildren;
+    int keep_alive;
+    wordlist *authenticate;
+};
+
+typedef struct _negotiate_user negotiate_user_t;
+typedef struct _negotiate_request negotiate_request_t;
+typedef struct _negotiate_helper_state_t negotiate_helper_state_t;
+typedef struct _auth_negotiate_config auth_negotiate_config;
+
+extern MemPool *negotiate_user_pool;
+extern MemPool *negotiate_request_pool;
+
+#endif
Index: squid/src/auth/ntlm/auth_ntlm.c
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/auth/ntlm/auth_ntlm.c,v
retrieving revision 1.18.2.14
retrieving revision 1.18.2.14.2.1
diff -u -r1.18.2.14 -r1.18.2.14.2.1
--- squid/src/auth/ntlm/auth_ntlm.c	8 Dec 2004 03:16:19 -0000	1.18.2.14
+++ squid/src/auth/ntlm/auth_ntlm.c	12 Jan 2005 23:09:05 -0000	1.18.2.14.2.1
@@ -53,7 +53,6 @@
 
 /* NTLM Scheme */
 static HLPSCB authenticateNTLMHandleReply;
-static HLPSCB authenticateNTLMHandleplaceholder;
 static AUTHSACTIVE authenticateNTLMActive;
 static AUTHSAUTHED authNTLMAuthenticated;
 static AUTHSAUTHUSER authenticateNTLMAuthenticateUser;
@@ -66,7 +65,6 @@
 static AUTHSFREECONFIG authNTLMFreeConfig;
 static AUTHSINIT authNTLMInit;
 static AUTHSONCLOSEC authenticateNTLMOnCloseConnection;
-static AUTHSCONNLASTHEADER NTLMLastHeader;
 static AUTHSUSERNAME authenticateNTLMUsername;
 static AUTHSREQFREE authNTLMAURequestFree;
 static AUTHSPARSE authNTLMParse;
@@ -74,24 +72,16 @@
 static AUTHSSTATS authenticateNTLMStats;
 static AUTHSSHUTDOWN authNTLMDone;
 
-/* helper callbacks to handle per server state data */
-static HLPSAVAIL authenticateNTLMHelperServerAvailable;
-static HLPSRESET authenticateNTLMHelperServerReset;
-
 static statefulhelper *ntlmauthenticators = NULL;
 
 CBDATA_TYPE(authenticateStateData);
 
 static int authntlm_initialised = 0;
 
-static MemPool *ntlm_helper_state_pool = NULL;
 static MemPool *ntlm_user_pool = NULL;
 static MemPool *ntlm_request_pool = NULL;
-static MemPool *ntlm_challenge_pool = NULL;
 static auth_ntlm_config *ntlmConfig = NULL;
 
-static hash_table *ntlm_challenge_cache = NULL;
-
 static void authenticateNTLMReleaseServer(ntlm_request_t * ntlm_request);
 /*
  *
@@ -111,11 +101,6 @@
     if (ntlmauthenticators)
 	helperStatefulFree(ntlmauthenticators);
     ntlmauthenticators = NULL;
-    if (ntlm_helper_state_pool) {
-	assert(memPoolInUseCount(ntlm_helper_state_pool) == 0);
-	memPoolDestroy(ntlm_helper_state_pool);
-	ntlm_helper_state_pool = NULL;
-    }
     if (ntlm_request_pool) {
 	assert(memPoolInUseCount(ntlm_request_pool) == 0);
 	memPoolDestroy(ntlm_request_pool);
@@ -152,12 +137,10 @@
 	storeAppendPrintf(entry, " %s", list->key);
 	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%s %s use_ntlm_negotiate %s\n",
-	name, "ntlm", config->authenticateChildren,
-	name, "ntlm", config->challengeuses,
-	name, "ntlm", (int) config->challengelifetime,
-	name, "ntlm", config->use_ntlm_negotiate ? "on" : "off");
-
+    storeAppendPrintf(entry, "\n");
+    storeAppendPrintf(entry, "%s %s children %d\n", name, "ntlm", config->authenticateChildren);
+    storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "ntlm", config->keep_alive ? "on" : "off");
+    storeAppendPrintf(entry, "%s %s use_ntlm_negotiate %s\n", name, "ntlm", config->use_ntlm_negotiate ? "on" : "off");
 }
 
 static void
@@ -170,9 +153,6 @@
 	memset(scheme->scheme_data, 0, sizeof(auth_ntlm_config));
 	ntlmConfig = scheme->scheme_data;
 	ntlmConfig->authenticateChildren = 5;
-	ntlmConfig->challengeuses = 0;
-	ntlmConfig->challengelifetime = 60;
-	ntlmConfig->use_ntlm_negotiate = 0;
     }
     ntlmConfig = scheme->scheme_data;
     if (strcasecmp(param_str, "program") == 0) {
@@ -182,10 +162,8 @@
 	requirePathnameExists("authparam ntlm program", ntlmConfig->authenticate->key);
     } else if (strcasecmp(param_str, "children") == 0) {
 	parse_int(&ntlmConfig->authenticateChildren);
-    } else if (strcasecmp(param_str, "max_challenge_reuses") == 0) {
-	parse_int(&ntlmConfig->challengeuses);
-    } else if (strcasecmp(param_str, "max_challenge_lifetime") == 0) {
-	parse_time_t(&ntlmConfig->challengelifetime);
+    } else if (strcasecmp(param_str, "keep_alive") == 0) {
+	parse_onoff(&ntlmConfig->keep_alive);
     } else if (strcasecmp(param_str, "use_ntlm_negotiate") == 0) {
 	parse_onoff(&ntlmConfig->use_ntlm_negotiate);
     } else {
@@ -201,11 +179,6 @@
      */
     if (ntlmConfig->authenticate && Config.onoff.pipeline_prefetch != 0)
 	Config.onoff.pipeline_prefetch = 0;
-
-    if (ntlmConfig->use_ntlm_negotiate && ntlmConfig->challengeuses > 0) {
-	debug(28, 1) ("challenge reuses incompatible with use_ntlm_negotiate. Disabling challenge reuse\n");
-	ntlmConfig->challengeuses = 0;
-    }
 }
 
 
@@ -231,7 +204,6 @@
     authscheme->decodeauth = authenticateDecodeNTLMAuth;
     authscheme->donefunc = authNTLMDone;
     authscheme->oncloseconnection = authenticateNTLMOnCloseConnection;
-    authscheme->authConnLastHeader = NTLMLastHeader;
 }
 
 /* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
@@ -241,8 +213,6 @@
 {
     static int ntlminit = 0;
     if (ntlmConfig->authenticate) {
-	if (!ntlm_helper_state_pool)
-	    ntlm_helper_state_pool = memPoolCreate("NTLM Helper State data", sizeof(ntlm_helper_state_t));
 	if (!ntlm_user_pool)
 	    ntlm_user_pool = memPoolCreate("NTLM Scheme User Data", sizeof(ntlm_user_t));
 	if (!ntlm_request_pool)
@@ -250,27 +220,10 @@
 	authntlm_initialised = 1;
 	if (ntlmauthenticators == NULL)
 	    ntlmauthenticators = helperStatefulCreate("ntlmauthenticator");
-	if (ntlmConfig->challengeuses) {
-	    if (!ntlm_challenge_cache)
-		ntlm_challenge_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
-	    if (!ntlm_challenge_pool)
-		ntlm_challenge_pool = memPoolCreate("NTLM Challenge Cache", sizeof(ntlm_challenge_hash_pointer));
-	}
 	ntlmauthenticators->cmdline = ntlmConfig->authenticate;
 	ntlmauthenticators->n_to_start = ntlmConfig->authenticateChildren;
 	ntlmauthenticators->ipc_type = IPC_TCP_SOCKET;
-	ntlmauthenticators->datapool = ntlm_helper_state_pool;
-	ntlmauthenticators->IsAvailable = authenticateNTLMHelperServerAvailable;
-	ntlmauthenticators->Reset = authenticateNTLMHelperServerReset;
 	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) {
 	    cachemgrRegister("ntlmauthenticator",
 		"NTLM User Authenticator Stats",
@@ -291,12 +244,20 @@
 static int
 authNTLMConfigured()
 {
-    if ((ntlmConfig != NULL) && (ntlmConfig->authenticate != NULL) && (ntlmConfig->authenticateChildren != 0) && (ntlmConfig->challengeuses > -1) && (ntlmConfig->challengelifetime > -1)) {
-	debug(29, 9) ("authNTLMConfigured: returning configured\n");
-	return 1;
+    if (ntlmConfig == NULL) {
+	debug(29, 9) ("authNTLMConfigured: not configured\n");
+	return 0;
     }
-    debug(29, 9) ("authNTLMConfigured: returning unconfigured\n");
-    return 0;
+    if (ntlmConfig->authenticate == NULL) {
+	debug(29, 9) ("authNTLMConfigured: no helper\n");
+	return 0;
+    }
+    if (ntlmConfig->authenticateChildren == 0) {
+	debug(29, 9) ("authNTLMConfigured: no helper children\n");
+	return 0;
+    }
+    debug(29, 9) ("authNTLMConfigured: returning configured\n");
+    return 1;
 }
 
 /* NTLM Scheme */
@@ -306,19 +267,23 @@
 {
     ntlm_request_t *ntlm_request = auth_user_request->scheme_data;
     /* null auth_user is checked for by authenticateDirection */
+    if (ntlm_request->waiting || ntlm_request->client_blob)
+	return -1;			/* Need helper response to continue */
     switch (ntlm_request->auth_state) {
     case AUTHENTICATE_STATE_NONE:	/* no progress at all. */
 	debug(29, 1) ("authenticateNTLMDirection: called before NTLM Authenticate!. Report a bug to squid-dev. au %p\n", auth_user_request);
-	/* fall thru */
+	return 1;
+    case AUTHENTICATE_STATE_NEGOTIATE:	/* send to client */
+	assert(ntlm_request->server_blob);
+	return 1;
     case AUTHENTICATE_STATE_FAILED:
 	return -2;
-    case AUTHENTICATE_STATE_NEGOTIATE:		/* send to helper */
-    case AUTHENTICATE_STATE_RESPONSE:	/*send to helper */
-	return -1;
-    case AUTHENTICATE_STATE_CHALLENGE:		/* send to client */
-	return 1;
+    case AUTHENTICATE_STATE_FINISHED:	/* do nothing.. */
     case AUTHENTICATE_STATE_DONE:	/* do nothing.. */
 	return 0;
+    case AUTHENTICATE_STATE_INITIAL:
+	debug(29, 1) ("authenticateNTLMDirection: Unexpected AUTHENTICATE_STATE_INITIAL\n");
+	return -2;
     }
     return -2;
 }
@@ -332,40 +297,44 @@
 authenticateNTLMFixErrorHeader(auth_user_request_t * auth_user_request, HttpReply * rep, http_hdr_type type, request_t * request)
 {
     ntlm_request_t *ntlm_request;
-    if (ntlmConfig->authenticate) {
-	/* New request, no user details */
-	if (auth_user_request == NULL) {
-	    debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
-	    httpHeaderPutStrf(&rep->header, type, "NTLM");
+    if (!ntlmConfig->authenticate)
+	return;
+    /* New request, no user details */
+    if (auth_user_request == NULL) {
+	debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type);
+	httpHeaderPutStrf(&rep->header, type, "NTLM");
+	if (!ntlmConfig->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 = auth_user_request->scheme_data;
-	    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);
-		break;
-	    default:
-		debug(29, 0) ("authenticateNTLMFixErrorHeader: state %d.\n", ntlm_request->auth_state);
-		fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
-	    }
 	}
+	return;
+    }
+    ntlm_request = auth_user_request->scheme_data;
+    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");
+	request->flags.proxy_keepalive = 0;
+	break;
+    case AUTHENTICATE_STATE_NEGOTIATE:
+	/* we are 'waiting' for a response from the client */
+	/* pass the blob to the client */
+	debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM %s'\n", type, ntlm_request->server_blob);
+	httpHeaderPutStrf(&rep->header, type, "NTLM %s", ntlm_request->server_blob);
+	safe_free(ntlm_request->server_blob);
+	break;
+    case AUTHENTICATE_STATE_FINISHED:
+	/* Special case when authentication finished, but not allowed by ACL */
+	debug(29, 9) ("authenticateNTLMFixErrorHeader: Sending type:%d header: 'NTLM'\n", type, ntlm_request->server_blob);
+	httpHeaderPutStrf(&rep->header, type, "NTLM");
+	break;
+    default:
+	debug(29, 0) ("authenticateNTLMFixErrorHeader: state %d.\n", ntlm_request->auth_state);
+	fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
     }
 }
 
@@ -374,12 +343,7 @@
 {
     if (!ntlm_request)
 	return;
-    if (ntlm_request->ntlmnegotiate)
-	xfree(ntlm_request->ntlmnegotiate);
-    if (ntlm_request->authchallenge)
-	xfree(ntlm_request->authchallenge);
-    if (ntlm_request->ntlmauthenticate)
-	xfree(ntlm_request->ntlmauthenticate);
+    safe_free(ntlm_request->server_blob);
     if (ntlm_request->authserver != NULL) {
 	debug(29, 9) ("authenticateNTLMRequestFree: releasing server '%p'\n", ntlm_request->authserver);
 	authenticateNTLMReleaseServer(ntlm_request);
@@ -395,8 +359,6 @@
     auth_user_request->scheme_data = NULL;
 }
 
-static void authenticateNTLMChallengeCacheRemoveLink(ntlm_challenge_hash_pointer * challenge_hash);
-
 static void
 authenticateNTLMFreeUser(auth_user_t * auth_user)
 {
@@ -405,10 +367,6 @@
     debug(29, 5) ("authenticateNTLMFreeUser: Clearing NTLM scheme data\n");
     if (ntlm_user->username)
 	xfree(ntlm_user->username);
-    /* were they linked in by one or more proxy-authenticate headers */
-    while (ntlm_user->challenge_list.head) {
-	authenticateNTLMChallengeCacheRemoveLink(ntlm_user->challenge_list.head->data);
-    }
     memPoolFree(ntlm_user_pool, ntlm_user);
     auth_user->scheme_data = NULL;
 }
@@ -418,60 +376,23 @@
 authenticateNTLMReleaseServer(ntlm_request_t * ntlm_request)
 {
     helper_stateful_server *server = ntlm_request->authserver;
+    if (!server)
+	return;
     debug(29, 9) ("authenticateNTLMReleaseServer: releasing server '%p'\n", server);
     ntlm_request->authserver = NULL;
-    if (!ntlmConfig->challengeuses) {
-	ntlm_helper_state_t *helperstate = helperStatefulServerGetData(server);
-	helperstate->starve = 1;
-    }
-    helperStatefulReleaseServer(server);
-}
-
-/* clear the NTLM helper of being reserved for future requests */
-static void
-authenticateNTLMResetServer(ntlm_request_t * ntlm_request)
-{
-    helper_stateful_server *server = ntlm_request->authserver;
-    ntlm_helper_state_t *helperstate = helperStatefulServerGetData(server);
-    debug(29, 9) ("authenticateNTLMResetServer: releasing server '%p'\n", server);
-    ntlm_request->authserver = NULL;
-    helperstate->starve = 1;
     helperStatefulReleaseServer(server);
 }
 
 static void
-authenticateNTLMHandleplaceholder(void *data, void *srv, char *reply)
-{
-    authenticateStateData *r = data;
-    int valid;
-    /* we should only be called for placeholder requests - which have no reply string */
-    assert(reply == NULL);
-    assert(r->auth_user_request);
-    /* standard callback stuff */
-    valid = cbdataValid(r->data);
-    if (!valid) {
-	debug(29, 2) ("AuthenticateNTLMHandlePlacheholder: invalid callback data.\n");
-	helperStatefulReleaseServer(srv);
-	return;
-    }
-    /* call authenticateNTLMStart to retry this request */
-    debug(29, 9) ("authenticateNTLMHandleplaceholder: calling authenticateNTLMStart\n");
-    helperStatefulReleaseServer(srv);
-    authenticateNTLMStart(r->auth_user_request, r->handler, r->data);
-    cbdataUnlock(r->data);
-    authenticateStateFree(r);
-}
-
-static void
 authenticateNTLMHandleReply(void *data, void *srv, char *reply)
 {
     authenticateStateData *r = data;
-    ntlm_helper_state_t *helperstate;
     int valid;
     auth_user_request_t *auth_user_request;
     auth_user_t *auth_user;
     ntlm_user_t *ntlm_user;
     ntlm_request_t *ntlm_request;
+    char *blob;
     debug(29, 9) ("authenticateNTLMHandleReply: Helper: '%p' {%s}\n", srv, reply ? reply : "<NULL>");
     valid = cbdataValid(r->data);
     if (!valid) {
@@ -487,99 +408,64 @@
 	 */
 	fatal("authenticateNTLMHandleReply: called with no result string\n");
     }
-    assert(r->auth_user_request != NULL);
-    assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
+
     auth_user_request = r->auth_user_request;
+
     ntlm_request = auth_user_request->scheme_data;
     assert(ntlm_request != NULL);
 
+    assert(ntlm_request->waiting);
+    ntlm_request->waiting = 0;
+    safe_free(ntlm_request->client_blob);
+
+    auth_user = auth_user_request->auth_user;
+    assert(auth_user != NULL);
+    assert(auth_user->auth_type == AUTH_NEGOTIATE);
+    ntlm_user = auth_user_request->auth_user->scheme_data;
+
+    if (ntlm_request->authserver == NULL)
+	ntlm_request->authserver = srv;
+    else
+	assert(ntlm_request->authserver == srv);
+
     /* 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 = helperStatefulServerGetData(srv);
-	if (helperstate == NULL)
-	    fatal("lost NTLM helper state! quitting\n");
-	helperstate->challenge = xstrdup(reply);
-	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(ntlm_request->auth_state == AUTHENTICATE_STATE_NEGOTIATE);
+    blob = strchr(reply, ' ');
+    
+    if (strncasecmp(reply, "TT ", 3) == 0 && blob != NULL) {
+	/* we have been given a blob to send to the client */
 	ntlm_request->authserver = srv;
-	ntlm_request->authchallenge = xstrdup(reply);
-	helperstate->challengeuses = 1;
-    } else if (strncasecmp(reply, "AF ", 3) == 0) {
+	safe_free(ntlm_request->server_blob);
+	ntlm_request->server_blob = xstrdup(blob);
+	ntlm_request->auth_state = AUTHENTICATE_STATE_NEGOTIATE;
+	safe_free(auth_user_request->message);
+	auth_user_request->message = xstrdup("Authenication in progress");
+	debug(29, 4) ("authenticateNTLMHandleReply: Need to challenge the client with a server blob '%s'\n", blob);
+    } else if (strncasecmp(reply, "AF ", 3) == 0 && arg != NULL) {
 	/* we're finished, release the helper */
-	reply += 3;
-	auth_user = auth_user_request->auth_user;
-	ntlm_user = auth_user_request->auth_user->scheme_data;
-	assert(ntlm_user != NULL);
-	/* we only expect OK when finishing the handshake */
-	assert(ntlm_request->auth_state == AUTHENTICATE_STATE_RESPONSE);
-	ntlm_user->username = xstrdup(reply);
-#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;
-	auth_user = auth_user_request->auth_user;
-	ntlm_user = auth_user_request->auth_user->scheme_data;
-	assert(ntlm_user != NULL);
-	/* we only expect LD when finishing the handshake */
-	assert(ntlm_request->auth_state == AUTHENTICATE_STATE_RESPONSE);
-	ntlm_user->username = xstrdup(reply);
-	helperstate = helperStatefulServerGetData(ntlm_request->authserver);
-	/* BH code: mark helper as broken */
-	authenticateNTLMResetServer(ntlm_request);
-	debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
-#endif
-    } else if (strncasecmp(reply, "NA ", 3) == 0) {
-	/* todo: action of Negotiate state on error */
+	safe_free(ntlm_user->username);
+	ntlm_user->username = xstrdup(blob);
+	safe_free(auth_user_request->message);
+	auth_user_request->message = xstrdup("Login successful");
+	debug(29, 4) ("authenticateNTLMHandleReply: Successfully validated user via NTLM. Username '%s'\n", arg);
+	authenticateNTLMReleaseServer(ntlm_request);
+	ntlm_request->auth_state = AUTHENTICATE_STATE_FINISHED;
+    } else if (strncasecmp(reply, "NA ", 3) == 0 && arg != NULL) {
+	safe_free(auth_user_request->message);
+	auth_user_request->message = xstrdup(blob);
 	ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
-	authenticateNTLMResetServer(ntlm_request);
-	debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
+	authenticateNTLMReleaseServer(ntlm_request);
+	debug(29, 4) ("authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '%s'\n", arg);
     } else if (strncasecmp(reply, "BH ", 3) == 0) {
 	/* TODO kick off a refresh process. This can occur after a YR or after
 	 * a KK. If after a YR release the helper and resubmit the request via 
 	 * Authenticate NTLM start. 
 	 * If after a KK deny the user's request w/ 407 and mark the helper as 
 	 * Needing YR. */
-	assert(r->auth_user_request != NULL);
-	assert(r->auth_user_request->auth_user->auth_type == AUTH_NTLM);
-	auth_user_request = r->auth_user_request;
-	auth_user = auth_user_request->auth_user;
-	assert(auth_user != NULL);
-	ntlm_user = auth_user->scheme_data;
-	ntlm_request = auth_user_request->scheme_data;
-	assert((ntlm_user != NULL) && (ntlm_request != NULL));
-	assert(!ntlm_request->authserver || ntlm_request->authserver == srv);
-	helperstate = helperStatefulServerGetData(ntlm_request->authserver);
-	authenticateNTLMResetServer(ntlm_request);
-	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", srv, reply);
-	    /* resubmit the request. This helper is currently busy, so we will get
-	     * a different one. Our auth state stays the same */
-	    authenticateNTLMStart(auth_user_request, r->handler, r->data);
-	    /* don't call the callback */
-	    cbdataUnlock(r->data);
-	    authenticateStateFree(r);
-	    return;
-	} else {
-	    /* the helper broke on a KK */
-	    debug(29, 1) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
-	    ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
-	}
+	auth_user_request->message = xstrdup(reply);
+	ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
+	safe_free(ntlm_request->server_blob);
+	authenticateNTLMReleaseServer(ntlm_request);
+	debug(29, 1) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
     } else {
 	fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
     }
@@ -595,48 +481,11 @@
     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 */
 static void
 authenticateNTLMStart(auth_user_request_t * auth_user_request, RH * handler, void *data)
 {
     authenticateStateData *r = NULL;
-    helper_stateful_server *server;
-    ntlm_helper_state_t *helperstate;
     char buf[8192];
     char *sent_string = NULL;
     ntlm_user_t *ntlm_user;
@@ -651,26 +500,9 @@
     assert(ntlm_request);
     assert(handler);
     assert(data);
-    assert(auth_user->auth_type == AUTH_NTLM);
+    assert(auth_user->auth_type == AUTH_NEGOTIATE);
     debug(29, 9) ("authenticateNTLMStart: auth state '%d'\n", ntlm_request->auth_state);
-    switch (ntlm_request->auth_state) {
-    case AUTHENTICATE_STATE_NEGOTIATE:
-	sent_string = ntlm_request->ntlmnegotiate;
-	break;
-    case AUTHENTICATE_STATE_RESPONSE:
-	sent_string = ntlm_request->ntlmauthenticate;
-	assert(ntlm_request->authserver);
-	debug(29, 9) ("authenticateNTLMStart: Asking NTLMauthenticator '%p'.\n", ntlm_request->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++;
+    sent_string = ntlm_request->client_blob;
 
     debug(29, 9) ("authenticateNTLMStart: state '%d'\n", ntlm_request->auth_state);
     debug(29, 9) ("authenticateNTLMStart: '%s'\n", sent_string);
@@ -679,120 +511,25 @@
 	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 (ntlm_request->auth_state) {
-    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 = helperStatefulGetServer(ntlmauthenticators);
-	helperstate = server ? helperStatefulServerGetData(server) : NULL;
-	if (ntlmConfig->challengeuses) {
-	    while ((server != NULL) && authenticateNTLMChangeChallenge_p(helperstate)) {
-		/* flag this helper for challenge changing */
-		helperstate->starve = 1;
-		helperStatefulReleaseServer(server);
-		/* Get another server */
-		server = helperStatefulGetServer(ntlmauthenticators);
-		helperstate = server ? helperStatefulServerGetData(server) : NULL;
-	    }
-	}
-	ntlm_request->authserver = server;
-	/* tell the log what helper we have been given */
-	if (server == NULL)
-	    debug(29, 9) ("authenticateNTLMStart: unable to get a ntlm helper... Queuing as a placeholder request.\n");
-	else
-	    debug(29, 9) ("authenticateNTLMStart: helper '%p' assigned\n", server);
-	/* server and valid challenge? */
-	if ((server == NULL) || ntlmConfig->challengeuses == 0 || !authenticateNTLMValidChallenge(helperstate)) {
-	    /* No server, or server with invalid challenge */
-	    r = cbdataAlloc(authenticateStateData);
-	    r->handler = handler;
-	    cbdataLock(data);
-	    r->data = data;
-	    r->auth_user_request = auth_user_request;
-	    authenticateAuthUserRequestLock(r->auth_user_request);
-	    if (server == NULL && ntlmConfig->challengeuses) {
-		helperStatefulSubmit(ntlmauthenticators, NULL, authenticateNTLMHandleplaceholder, r, NULL);
-	    } else {
-		/* Server with invalid challenge */
-
-		if (ntlmConfig->use_ntlm_negotiate) {
-		    snprintf(buf, 8192, "YR %s\n", sent_string);
-		} else {
-		    snprintf(buf, 8192, "YR\n");
-		}
 
-		helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, ntlm_request->authserver);
-	    }
-	} else {
-	    if (!ntlmConfig->challengeuses)
-		debug(29, 0) ("authenticateNTLMStart: Reused challenge in server %p even if challenge reuse is disabled!", server);
-	    /* (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 */
-	    ntlm_request->authchallenge = xstrdup(helperstate->challenge);
-	    handler(data, NULL);
-	}
-
-	break;
-    case AUTHENTICATE_STATE_RESPONSE:
-	r = cbdataAlloc(authenticateStateData);
-	r->handler = handler;
-	cbdataLock(data);
-	r->data = data;
-	r->auth_user_request = auth_user_request;
-	authenticateAuthUserRequestLock(r->auth_user_request);
+    /* Send blob to helper */
+    r = cbdataAlloc(authenticateStateData);
+    r->handler = handler;
+    cbdataLock(data);
+    r->data = data;
+    r->auth_user_request = auth_user_request;
+    authenticateAuthUserRequestLock(r->auth_user_request);
+    if (ntlm_request->auth_state == AUTHENTICATE_STATE_INITIAL) {
+	if (ntlmConfig->use_ntlm_negotiate)
+	    snprintf(buf, 8192, "YR %s\n", sent_string);
+	else
+	    snprintf(buf, 8192, "YR\n", sent_string);
+    } else {
 	snprintf(buf, 8192, "KK %s\n", sent_string);
-	helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, ntlm_request->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 = data;
-    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;
-	}
-    }
-    debug(29, 4) ("authenticateNTLMHelperServerAvailable: no state data - returning 0\n");
-    return 0;
-}
-
-static void
-authenticateNTLMHelperServerReset(void *data)
-{
-    ntlm_helper_state_t *statedata = data;
-    if (statedata == NULL)
-	return;
-    if (statedata->starve) {
-	/* we have been starving the helper */
-	debug(29, 9) ("authenticateNTLMHelperServerReset: resetting challenge details\n");
-	statedata->starve = 0;
-	statedata->challengeuses = 0;
-	statedata->renewed = 0;
-	xfree(statedata->challenge);
-	statedata->challenge = NULL;
-	while (statedata->user_list.head) {
-	    authenticateNTLMChallengeCacheRemoveLink(statedata->user_list.head->data);
-	}
     }
+    ntlm_request->waiting = 1;
+    safe_free(ntlm_request->client_blob);
+    helperStatefulSubmit(ntlmauthenticators, buf, authenticateNTLMHandleReply, r, ntlm_request->authserver);
 }
 
 /* clear any connection related authentication details */
@@ -830,22 +567,8 @@
     return NULL;
 }
 
-/* NTLMLastHeader: return a pointer to the last header used in authenticating
- * the request/conneciton
- */
-static const char *
-NTLMLastHeader(auth_user_request_t * auth_user_request)
-{
-    ntlm_request_t *ntlm_request;
-    assert(auth_user_request != NULL);
-    assert(auth_user_request->scheme_data != NULL);
-    ntlm_request = auth_user_request->scheme_data;
-    return ntlm_request->ntlmauthenticate;
-}
-
 /*
- * Decode an NTLM [Proxy-]Auth string, placing the results in the passed
- * Auth_user structure.
+ * Called on the initial request only, to set things up for later processing
  */
 
 static void
@@ -854,7 +577,7 @@
     dlink_node *node;
     assert(auth_user_request->auth_user == NULL);
     auth_user_request->auth_user = authenticateAuthUserNew("ntlm");
-    auth_user_request->auth_user->auth_type = AUTH_NTLM;
+    auth_user_request->auth_user->auth_type = AUTH_NEGOTIATE;
     auth_user_request->auth_user->scheme_data = memPoolAlloc(ntlm_user_pool);
     auth_user_request->scheme_data = memPoolAlloc(ntlm_request_pool);
     memset(auth_user_request->scheme_data, '\0', sizeof(ntlm_request_t));
@@ -863,7 +586,8 @@
     node = dlinkNodeNew();
     dlinkAdd(auth_user_request, node, &auth_user_request->auth_user->requests);
 
-    /* all we have to do is identify that it's NTLM - the helper does the rest */
+    /* the helper does the rest, with data collected in
+     * authenticateNTLMAuthenticateUser */
     debug(29, 9) ("authenticateDecodeNTLMAuth: NTLM authentication\n");
     return;
 }
@@ -875,47 +599,11 @@
 }
 
 
-/* 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
-authenticateNTLMChallengeCacheAddLink(const char *key, auth_user_t * auth_user, helper_stateful_server * auth_server)
-{
-    ntlm_challenge_hash_pointer *challenge_hash;
-    ntlm_user_t *ntlm_user;
-    ntlm_helper_state_t *helperstate = helperStatefulServerGetData(auth_server);
-    ntlm_user = auth_user->scheme_data;
-    /* prevent duplicates */
-    if (hash_lookup(ntlm_challenge_cache, key))
-	return;
-    challenge_hash = memPoolAlloc(ntlm_challenge_pool);
-    challenge_hash->key = xstrdup(key);
-    challenge_hash->user.auth_user = auth_user;
-    dlinkAddTail(challenge_hash, &challenge_hash->user.link, &ntlm_user->challenge_list);
-    challenge_hash->challenge.authserver = auth_server;
-    dlinkAddTail(challenge_hash, &challenge_hash->challenge.link, &helperstate->user_list);
-    hash_join(ntlm_challenge_cache, (hash_link *) challenge_hash);
-}
-
-static void
-authenticateNTLMChallengeCacheRemoveLink(ntlm_challenge_hash_pointer * challenge_hash)
-{
-    ntlm_user_t *ntlm_user = challenge_hash->user.auth_user->scheme_data;
-    ntlm_helper_state_t *helperstate = helperStatefulServerGetData(challenge_hash->challenge.authserver);
-    hash_remove_link(ntlm_challenge_cache, (hash_link *) challenge_hash);
-    dlinkDelete(&challenge_hash->user.link, &ntlm_user->challenge_list);
-    dlinkDelete(&challenge_hash->challenge.link, &helperstate->user_list);
-    xfree(challenge_hash->key);
-    memPoolFree(ntlm_challenge_pool, challenge_hash);
-}
-
-
 static int
 authNTLMAuthenticated(auth_user_request_t * auth_user_request)
 {
     ntlm_request_t *ntlm_request = auth_user_request->scheme_data;
-    if (ntlm_request->auth_state == AUTHENTICATE_STATE_DONE)
+    if (ntlm_request->auth_state == AUTHENTICATE_STATE_FINISHED)
 	return 1;
     debug(29, 9) ("User not fully authenticated.\n");
     return 0;
@@ -924,19 +612,15 @@
 static void
 authenticateNTLMAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type)
 {
-    const char *proxy_auth;
+    const char *proxy_auth, *blob;
     auth_user_hash_pointer *usernamehash;
-    ntlm_challenge_hash_pointer *challenge_hash = NULL;
     auth_user_t *auth_user;
     ntlm_request_t *ntlm_request;
     ntlm_user_t *ntlm_user;
-    LOCAL_ARRAY(char, ntlmhash, NTLM_CHALLENGE_SZ * 2);
-    /* get header */
-    proxy_auth = httpHeaderGetStr(&request->header, type);
 
     auth_user = auth_user_request->auth_user;
     assert(auth_user);
-    assert(auth_user->auth_type == AUTH_NTLM);
+    assert(auth_user->auth_type == AUTH_NEGOTIATE);
     assert(auth_user->scheme_data != NULL);
     assert(auth_user_request->scheme_data != NULL);
     ntlm_user = auth_user->scheme_data;
@@ -948,13 +632,33 @@
 	debug(29, 1) ("authenticateNTLMAuthenticateUser: attempt to perform authentication without a connection!\n");
 	return;
     }
+    if (ntlm_request->waiting) {
+	debug(29, 1) ("authenticateNTLMAuthenticateUser: waiting for helper reply!\n");
+	return;
+    }
+    if (ntlm_request->server_blob) {
+	debug(29, 2) ("authenticateNTLMAuthenticateUser: need to challenge client '%s'!\n", ntlm_request->server_blob);
+	return;
+    }
+
+    /* get header */
+    proxy_auth = httpHeaderGetStr(&request->header, type);
+    if (strncasecmp("NTLM ", proxy_auth, 5) != 0) {
+	fatal("Incorrect scheme in auth header\n");
+	/* TODO: more fault tolerance.. reset the auth scheme here */
+    }
+    blob = proxy_auth + 10;
+    while (xisspace(*blob))      /*trim leading spaces */
+	blob++;
+
     switch (ntlm_request->auth_state) {
     case AUTHENTICATE_STATE_NONE:
-	/* we've recieved a negotiate request. pass to a helper */
+	/* we've recieved a ntlm 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);
-	conn->auth_type = AUTH_NTLM;
+	ntlm_request->auth_state = AUTHENTICATE_STATE_INITIAL;
+	safe_free(ntlm_request->client_blob);
+	ntlm_request->client_blob = xstrdup(blob);
+	conn->auth_type = AUTH_NEGOTIATE;
 	conn->auth_user_request = auth_user_request;
 	ntlm_request->conn = conn;
 	/* and lock for the connection duration */
@@ -962,73 +666,24 @@
 	authenticateAuthUserRequestLock(auth_user_request);
 	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) ("authenticateNTLMAuthenticateUser: need to ask helper!\n");
 	return;
 	break;
-    case AUTHENTICATE_STATE_CHALLENGE:
-	/* we should have recieved a NTLM challenge. pass it to the same 
+    case AUTHENTICATE_STATE_NEGOTIATE:
+	/* we should have recieved a blob from the clien. 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. */
-	if (strncmp("NTLM ", proxy_auth, 5) == 0) {
-	    ntlm_request->ntlmauthenticate = xstrdup(proxy_auth);
-	} else {
-	    fatal("Incorrect scheme in auth header\n");
-	    /* TODO: more fault tolerance.. reset the auth scheme here */
-	}
-	/* normal case with challenge reuses disabled */
-	if (ntlmConfig->challengeuses == 0) {
-	    /* verify with the ntlm helper */
-	    ntlm_request->auth_state = AUTHENTICATE_STATE_RESPONSE;
-	    return;
-	}
-	/* cache entries have authenticateauthheaderchallengestring */
-	snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
-	    ntlm_request->ntlmauthenticate,
-	    ntlm_request->authchallenge);
-	/* see if we already know this user's authenticate */
-	debug(29, 9) ("aclMatchProxyAuth: cache lookup with key '%s'\n", ntlmhash);
-	assert(ntlm_challenge_cache != NULL);
-	challenge_hash = hash_lookup(ntlm_challenge_cache, ntlmhash);
-	if (!challenge_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(ntlm_request);
-	    authenticateAuthUserMerge(auth_user, challenge_hash->user.auth_user);
-	    auth_user = challenge_hash->user.auth_user;
-	    auth_user_request->auth_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 = auth_user->scheme_data;
-	    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;
-	}
+	safe_free(ntlm_request->client_blob);
+	ntlm_request->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;
+    case AUTHENTICATE_STATE_FINISHED:
 	/* this connection is authenticated */
-	debug(29, 4) ("authenticated\nch    %s\nauth     %s\nauthuser %s\n",
-	    ntlm_request->authchallenge,
-	    ntlm_request->ntlmauthenticate,
-	    ntlm_user->username);
+	debug(29, 4) ("authenticated user %s\n", ntlm_user->username);
 	/* see if this is an existing user with a different proxy_auth 
 	 * string */
 	usernamehash = hash_lookup(proxy_auth_username_cache, ntlm_user->username);
@@ -1047,17 +702,11 @@
 	    /* store user in hash's */
 	    authenticateUserNameCacheAdd(auth_user);
 	}
-	if (ntlmConfig->challengeuses) {
-	    /* cache entries have authenticateauthheaderchallengestring */
-	    snprintf(ntlmhash, sizeof(ntlmhash) - 1, "%s%s",
-		ntlm_request->ntlmauthenticate,
-		ntlm_request->authchallenge);
-	    authenticateNTLMChallengeCacheAddLink(ntlmhash, auth_user, ntlm_request->authserver);
-	}
 	/* 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;
 	authenticateNTLMReleaseServer(ntlm_request);
+	ntlm_request->auth_state = AUTHENTICATE_STATE_DONE;
 	return;
     case AUTHENTICATE_STATE_DONE:
 	fatal("authenticateNTLMAuthenticateUser: unexpect auth state DONE! Report a bug to the squid developers.\n");
Index: squid/src/auth/ntlm/auth_ntlm.h
===================================================================
RCS file: /cvsroot/squid-sf//squid/src/auth/ntlm/auth_ntlm.h,v
retrieving revision 1.8.2.4
retrieving revision 1.8.2.4.2.1
diff -u -r1.8.2.4 -r1.8.2.4.2.1
--- squid/src/auth/ntlm/auth_ntlm.h	26 Aug 2004 02:13:44 -0000	1.8.2.4
+++ squid/src/auth/ntlm/auth_ntlm.h	12 Jan 2005 23:09:07 -0000	1.8.2.4.2.1
@@ -10,9 +10,9 @@
 
 typedef enum {
     AUTHENTICATE_STATE_NONE,
+    AUTHENTICATE_STATE_INITIAL,
     AUTHENTICATE_STATE_NEGOTIATE,
-    AUTHENTICATE_STATE_CHALLENGE,
-    AUTHENTICATE_STATE_RESPONSE,
+    AUTHENTICATE_STATE_FINISHED,
     AUTHENTICATE_STATE_DONE,
     AUTHENTICATE_STATE_FAILED
 } auth_state_t;			/* connection level auth state */
@@ -27,65 +27,36 @@
 struct _ntlm_user {
     /* what username did this connection get? */
     char *username;
-    dlink_list challenge_list;
 };
 
-struct _ntlm_challenge_hash_pointer {
-    /* first two items must be same as hash_link */
-    char *key;
-    auth_user_hash_pointer *next;
-    struct {
-	auth_user_t *auth_user;
-	dlink_node link;	/* other hash entries that point to the same auth_user */
-    } user;
-    struct {
-	helper_stateful_server *authserver;
-	dlink_node link;	/* other hash entries that point to the same challenge */
-    } challenge;
-};
-
-
 struct _ntlm_request {
-    /* 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 */
+    /*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;
+    /* currently waiting for helper response */
+    int waiting;
     /* what connection is this associated with */
     ConnStateData *conn;
-};
-
-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;
-    dlink_list user_list;	/* ntlm_challenge_hash_pointer list referring to this challenge */
+    /* our current blob to pass to the client */
+    char *server_blob;
+    /* our current blob to pass to the server */
+    char *client_blob;
 };
 
 /* configuration runtime data */
 struct _auth_ntlm_config {
     int authenticateChildren;
-    wordlist *authenticate;
-    int challengeuses;
-    time_t challengelifetime;
+    int keep_alive;
     int use_ntlm_negotiate;
+    wordlist *authenticate;
 };
 
 typedef struct _ntlm_user ntlm_user_t;
 typedef struct _ntlm_request ntlm_request_t;
 typedef struct _ntlm_helper_state_t ntlm_helper_state_t;
 typedef struct _auth_ntlm_config auth_ntlm_config;
-typedef struct _ntlm_challenge_hash_pointer ntlm_challenge_hash_pointer;
 
-extern MemPool *ntlm_helper_state_pool;
 extern MemPool *ntlm_user_pool;
 extern MemPool *ntlm_request_pool;
 
