This patch is generated from the negotiate-nt-2_5 branch of nt-2_5 in squid
Mon May 22 00:16:59 2006 GMT
See http://devel.squid-cache.org/

Index: squid/ChangeLog
diff -u squid/ChangeLog:1.12.6.5.4.89 squid/ChangeLog:1.12.6.5.4.81.2.6
--- squid/ChangeLog:1.12.6.5.4.89	Sat May 20 23:13:54 2006
+++ squid/ChangeLog	Sun May 21 03:04:30 2006
@@ -57,7 +57,6 @@
 	- [Cosmetic] Add additinal tracing to squid_ldap_auth making
 	  diagnostics easier on squid_ldap_auth configuration errors
 	  (Bug #1395)
-	- [Minor] $HOME not set when started as root (Bug #1401)
 	- [Minor] httpd_accel_single_host breaks in combination with
 	  server_persistent_connections (Bug #1402)
 	- [Cosmetic] Setting CACHE_HTTP_PORT to configure was only partially
Index: squid/configure.in
diff -u squid/configure.in:1.42.2.5.4.89 squid/configure.in:1.42.2.5.4.81.2.6
--- squid/configure.in:1.42.2.5.4.89	Sat May 20 23:13:54 2006
+++ squid/configure.in	Sun May 21 03:04:30 2006
@@ -9,7 +9,7 @@
 dnl
 AC_INIT(src/main.c)
 AC_CONFIG_AUX_DIR(cfgaux)
-AM_INIT_AUTOMAKE(squid, 2.5.STABLE14-NT-CVS)
+AM_INIT_AUTOMAKE(squid, 2.5.STABLE14-NEGOTIATE-NT-CVS)
 AM_CONFIG_HEADER(include/autoconf.h)
 AC_REVISION($Revision$)dnl
 AC_PREFIX_DEFAULT(/usr/local/squid)
@@ -2634,6 +2634,7 @@
 	src/auth/basic/Makefile \
 	src/auth/digest/Makefile \
 	src/auth/ntlm/Makefile \
+	src/auth/negotiate/Makefile \
 	contrib/Makefile \
 	snmplib/Makefile \
 	icons/Makefile \
@@ -2669,6 +2670,8 @@
 	helpers/ntlm_auth/SMB/smbval/Makefile \
 	helpers/ntlm_auth/NTLMSSP-WIN32/Makefile \
 	helpers/ntlm_auth/winbind/Makefile \
+	helpers/negotiate_auth/Makefile \
+	helpers/negotiate_auth/Win32/Makefile \
 	helpers/external_acl/Makefile \
 	helpers/external_acl/ip_user/Makefile \
 	helpers/external_acl/ldap_group/Makefile \
Index: squid/helpers/Makefile.am
diff -u squid/helpers/Makefile.am:1.2.8.2 squid/helpers/Makefile.am:1.2.8.2.2.1
--- squid/helpers/Makefile.am:1.2.8.2	Fri Jul 12 23:27:44 2002
+++ squid/helpers/Makefile.am	Sat Oct 22 10:45:23 2005
@@ -1 +1 @@
-SUBDIRS = basic_auth ntlm_auth digest_auth external_acl
+SUBDIRS = basic_auth ntlm_auth digest_auth negotiate_auth external_acl
Index: squid/helpers/basic_auth/win32_locallogon/NT_auth.c
diff -u squid/helpers/basic_auth/win32_locallogon/NT_auth.c:1.1.4.7 squid/helpers/basic_auth/win32_locallogon/NT_auth.c:1.1.4.7.2.1
--- squid/helpers/basic_auth/win32_locallogon/NT_auth.c:1.1.4.7	Wed Sep 14 05:12:21 2005
+++ squid/helpers/basic_auth/win32_locallogon/NT_auth.c	Sat Oct 22 10:45:23 2005
@@ -120,7 +120,7 @@
 
     debug("%s build " __DATE__ ", " __TIME__ " starting up...\n", my_program_name);
 
-    if (LoadSecurityDll(SSP_BASIC) == NULL) {
+    if (LoadSecurityDll(SSP_BASIC, NTLM_PACKAGE_NAME) == NULL) {
 	fprintf(stderr, "FATAL, can't initialize SSPI, exiting.\n");
 	exit(1);
     }
Index: squid/helpers/negotiate_auth/.cvsignore
diff -u /dev/null squid/helpers/negotiate_auth/.cvsignore:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/negotiate_auth/.cvsignore	Sat Oct 22 10:45:23 2005
@@ -0,0 +1,2 @@
+.cvsignore
+Makefile.in
Index: squid/helpers/negotiate_auth/Makefile.am
diff -u /dev/null squid/helpers/negotiate_auth/Makefile.am:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/negotiate_auth/Makefile.am	Sat Oct 22 10:45:23 2005
@@ -0,0 +1,7 @@
+#  Makefile for storage modules in the Squid Object Cache server
+#
+#  $Id$
+#
+
+DIST_SUBDIRS	= Win32
+SUBDIRS		= @NEGOTIATE_AUTH_HELPERS@
Index: squid/helpers/negotiate_auth/Win32/.cvsignore
diff -u /dev/null squid/helpers/negotiate_auth/Win32/.cvsignore:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/negotiate_auth/Win32/.cvsignore	Sat Oct 22 10:45:23 2005
@@ -0,0 +1,2 @@
+.cvsignore
+Makefile.in
Index: squid/helpers/negotiate_auth/Win32/Makefile.am
diff -u /dev/null squid/helpers/negotiate_auth/Win32/Makefile.am:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/negotiate_auth/Win32/Makefile.am	Sat Oct 22 10:45:23 2005
@@ -0,0 +1,20 @@
+#
+#  Makefile for the Squid Object Cache server
+#
+#  $Id$
+#
+
+libexec_PROGRAMS = win32_negotiate_auth
+
+win32_negotiate_auth_SOURCES = libnegotiatessp.c negotiate_auth.c negotiate.h
+
+if ENABLE_MINGW32SPECIFIC
+INCLUDES      = -I. -I$(top_srcdir)/port/win32/include -I$(top_srcdir)/include -I$(top_srcdir)/src
+else
+INCLUDES      = -I. -I$(top_srcdir)/include -I$(top_srcdir)/src
+endif
+
+LDADD		= -L$(top_builddir)/lib -lntlmauth -lsspwin32 -lnetapi32 \
+		  -ladvapi32 -lmiscutil $(CRYPTLIB) $(XTRA_LIBS)
+
+EXTRA_DIST = readme.txt
Index: squid/helpers/negotiate_auth/Win32/libnegotiatessp.c
diff -u /dev/null squid/helpers/negotiate_auth/Win32/libnegotiatessp.c:1.1.2.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/negotiate_auth/Win32/libnegotiatessp.c	Sun Oct 30 05:04:36 2005
@@ -0,0 +1,83 @@
+/*
+ * (C) 2002 Guido Serassio <serassio@libero.it>
+ * Based on previous work of Francesco Chemolli and Robert Collins
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * 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.
+ */
+
+typedef unsigned char uchar;
+
+#include "util.h"
+#include "negotiate.h"
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+void hex_dump(void *data, int size)
+{
+    /* dumps size bytes of *data to stdout. Looks like:
+     * [0000] 75 6E 6B 6E 6F 77 6E 20
+     *                  30 FF 00 00 00 00 39 00 unknown 0.....9.
+     * (in a single line of course)
+     */
+
+    if (!data)
+        return;
+
+    if (debug_enabled) {
+        unsigned char *p = data;
+        unsigned char c;
+        int n;
+        char bytestr[4] = {0};
+        char addrstr[10] = {0};
+        char hexstr[ 16*3 + 5] = {0};
+        char charstr[16*1 + 5] = {0};
+        for(n=1;n<=size;n++) {
+            if (n%16 == 1) {
+                /* store address for this line */
+                snprintf(addrstr, sizeof(addrstr), "%.4x",
+                   ((unsigned int)p-(unsigned int)data) );
+            }
+            
+            c = *p;
+            if (isalnum(c) == 0) {
+                c = '.';
+            }
+
+            /* store hex str (for left side) */
+            snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
+            strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);
+
+            /* store char str (for right side) */
+            snprintf(bytestr, sizeof(bytestr), "%c", c);
+            strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);
+
+            if(n%16 == 0) { 
+                /* line completed */
+                fprintf(stderr, "[%4.4s]   %-50.50s  %s\n", addrstr, hexstr, charstr);
+                hexstr[0] = 0;
+                charstr[0] = 0;
+            } else if(n%8 == 0) {
+                /* half line: add whitespaces */
+                strncat(hexstr, "  ", sizeof(hexstr)-strlen(hexstr)-1);
+                strncat(charstr, " ", sizeof(charstr)-strlen(charstr)-1);
+            }
+            p++; /* next byte */
+        }
+
+        if (strlen(hexstr) > 0) {
+            /* print rest of buffer if not empty */
+            fprintf(stderr, "[%4.4s]   %-50.50s  %s\n", addrstr, hexstr, charstr);
+        }
+    }
+}
+
Index: squid/helpers/negotiate_auth/Win32/negotiate.h
diff -u /dev/null squid/helpers/negotiate_auth/Win32/negotiate.h:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/negotiate_auth/Win32/negotiate.h	Sat Oct 22 10:45:23 2005
@@ -0,0 +1,116 @@
+/*
+ * (C) 2002 Guido Serassio <serassio@libero.it>
+ * Based on previous work of Francesco Chemolli, Robert Collins and Andrew Doran
+ *
+ * Distributed freely under the terms of the GNU General Public License,
+ * version 2. See the file COPYING for licensing details
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ */
+
+#ifndef _NTLM_H_
+#define _NTLM_H_
+
+#include "sspwin32.h"
+#include <windows.h>
+#include <sspi.h>
+#include <security.h>
+#include "ntlmauth.h"
+#undef debug
+
+/************* CONFIGURATION ***************/
+/*
+ * define this if you want debugging
+ */
+#ifndef DEBUG
+#define DEBUG
+#endif
+
+#define FAIL_DEBUG 0
+
+/************* END CONFIGURATION ***************/
+
+#include <sys/types.h>
+
+extern int debug_enabled;
+#if FAIL_DEBUG
+extern int fail_debug_enabled;
+#endif
+
+/* Debugging stuff */
+
+#ifdef __GNUC__			/* this is really a gcc-ism */
+#ifdef DEBUG
+#include <stdio.h>
+#include <unistd.h>
+static char *__foo;
+#define debug(X...) if (debug_enabled) { \
+                    fprintf(stderr,"ntlm-auth[%d](%s:%d): ", getpid(), \
+                    ((__foo=strrchr(__FILE__,'/'))==NULL?__FILE__:__foo+1),\
+                    __LINE__);\
+                    fprintf(stderr,X); }
+#else /* DEBUG */
+#define debug(X...)		/* */
+#endif /* DEBUG */
+#else /* __GNUC__ */
+static void
+debug(char *format,...) 
+{
+#ifdef DEBUG
+#ifdef _SQUID_MSWIN_
+#if FAIL_DEBUG
+    if (debug_enabled || fail_debug_enabled) {
+#else
+    if (debug_enabled) {
+#endif
+	va_list args;
+
+	va_start(args,format);
+	fprintf(stderr, "negotiate-auth[%d]: ",getpid());
+	vfprintf(stderr, format, args);
+	va_end(args);
+#if FAIL_DEBUG
+        fail_debug_enabled = 0;
+#endif
+    }
+#endif /* _SQUID_MSWIN_ */
+#endif /* DEBUG */
+}
+#endif /* __GNUC__ */
+
+
+/* A couple of harmless helper macros */
+#define SEND(X) debug("sending '%s' to squid\n",X); printf(X "\n");
+#ifdef __GNUC__
+#define SEND2(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
+#define SEND3(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
+#else
+/* no gcc, no debugging. varargs macros are a gcc extension */
+#define SEND2(X,Y) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y);
+#define SEND3(X,Y,Z) debug("sending '" X "' to squid\n",Y,Z); printf(X "\n",Y,Z);
+#endif
+
+extern int ntlm_errno;
+
+#define NTLM_NO_ERROR 0
+#define NTLM_SSPI_ERROR 1
+#define NTLM_BAD_NTGROUP 2
+#define NTLM_BAD_REQUEST 3
+
+#define NEGOTIATE_LENGTH 16
+
+extern void uc(char *);
+
+extern char *negotiate_check_auth(SSP_blobP auth, int auth_length);
+extern void hex_dump(void *, int);
+
+#define safe_free(x)	if (x) { free(x); x = NULL; }
+
+#endif /* _NTLM_H_ */
Index: squid/helpers/negotiate_auth/Win32/negotiate_auth.c
diff -u /dev/null squid/helpers/negotiate_auth/Win32/negotiate_auth.c:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/negotiate_auth/Win32/negotiate_auth.c	Sat Oct 22 10:45:23 2005
@@ -0,0 +1,316 @@
+/*
+ * win32_ntlm_auth: helper for NTLM Authentication for Squid Cache
+ *
+ * (C)2005 Guido Serassio - Acme Consulting S.r.l.
+ *
+ * Authors:
+ *  Guido Serassio <guido.serassio@acmeconsulting.it>
+ *  Acme Consulting S.r.l., Italy <http://www.acmeconsulting.it>
+ *
+ * With contributions from others mentioned in the change history section
+ * below.
+ *
+ * Based on previous work of Francesco Chemolli and Robert Collins.
+ *
+ * Dependencies: Windows NT4 SP4 and later.
+ *
+ * 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.
+ *
+ * History:
+ *
+ * Version 1.0
+ * 29-10-2005 Guido Serassio
+ *              First release.
+ *
+ *
+ */
+
+#include "util.h"
+#if HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include "negotiate.h"
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#define BUFFER_SIZE 10240
+
+int debug_enabled = 0;
+int Negotiate_packet_debug_enabled = 0;
+
+static int have_serverblob;
+
+/* makes a null-terminated string upper-case. Changes CONTENTS! */
+void
+uc(char *string)
+{
+    char *p = string, c;
+    while ((c = *p)) {
+	*p = toupper(c);
+	p++;
+    }
+}
+
+/* makes a null-terminated string lower-case. Changes CONTENTS! */
+static void
+lc(char *string)
+{
+    char *p = string, c;
+    while ((c = *p)) {
+	*p = tolower(c);
+	p++;
+    }
+}
+
+void
+helperfail(const char *reason)
+{
+#if FAIL_DEBUG
+    fail_debug_enabled =1;
+#endif
+    SEND2("BH %s", reason);
+}
+
+/*
+  options:
+  -d enable debugging.
+  -v enable verbose NTLM packet debugging.
+ */
+char *my_program_name = NULL;
+
+void
+usage()
+{
+    fprintf(stderr,
+	"Usage: %s [-d] [-v] [-h]\n"
+	" -d  enable debugging.\n"
+        " -v  enable verbose NTLM packet debugging.\n"
+	" -h  this message\n\n",
+	my_program_name);
+}
+
+
+void
+process_options(int argc, char *argv[])
+{
+    int opt, had_error = 0;
+
+    opterr =0;
+    while (-1 != (opt = getopt(argc, argv, "hdv"))) {
+	switch (opt) {
+	case 'd':
+	    debug_enabled = 1;
+	    break;
+	case 'v':
+	    debug_enabled = 1;
+	    Negotiate_packet_debug_enabled = 1;
+	    break;
+	case 'h':
+	    usage();
+	    exit(0);
+	case '?':
+	    opt = optopt;
+	    /* fall thru to default */
+	default:
+	    fprintf(stderr, "unknown option: -%c. Exiting\n", opt);
+	    usage();
+	    had_error = 1;
+	}
+    }
+    if (had_error)
+	exit(1);
+}
+
+int
+manage_request()
+{
+    char buf[BUFFER_SIZE];
+    char helper_command[3];
+    char *c, *decoded;
+    int plen, status;
+    int oversized = 0;
+    char * ErrorMessage;
+    static char cred[SSP_MAX_CRED_LEN+1];
+    BOOL Done = FALSE;
+
+try_again:
+    if (fgets(buf, BUFFER_SIZE, stdin) == NULL) 
+        return 0;
+
+    c = memchr(buf, '\n', BUFFER_SIZE);	/* safer against overrun than strchr */
+    if (c) {
+	if (oversized) {
+	    helperfail("illegal request received");
+	    fprintf(stderr, "Illegal request received: '%s'\n", buf);
+	    return 1;
+	}
+	*c = '\0';
+    } else {
+	fprintf(stderr, "No newline in '%s'\n", buf);
+	oversized = 1;
+	goto try_again;
+    }
+
+    if ((strlen(buf) > 3) && Negotiate_packet_debug_enabled) {
+        decoded = base64_decode(buf + 3);
+        strncpy(helper_command, buf, 2);
+        debug("Got '%s' from Squid with data:\n", helper_command);
+        hex_dump(decoded, ((strlen(buf) - 3) * 3) / 4);
+    } else
+        debug("Got '%s' from Squid\n", buf);
+
+    if (memcmp(buf, "YR ", 3) == 0) {	/* refresh-request */
+	/* figure out what we got */
+        decoded = base64_decode(buf + 3);
+	/*  Note: we don't need to manage memory at this point, since
+	 *  base64_decode returns a pointer to static storage.
+	 */
+	if (!decoded) {		/* decoding failure, return error */
+	    SEND("NA * Packet format error, couldn't base64-decode");
+	    return 1;
+	}
+	/* Obtain server blob against SSPI */
+        plen = (strlen(buf) - 3) * 3 / 4;		/* we only need it here. Optimization */
+        c = (char *) SSP_MakeNegotiateBlob(decoded, plen, &Done, &status, cred);
+
+        if (status == SSP_OK) {
+            if (Done) {
+	        lc(cred);		/* let's lowercase them for our convenience */
+                have_serverblob = 0;
+                Done = FALSE;
+                if (Negotiate_packet_debug_enabled) {
+                    printf("AF %s %s\n",c,cred);
+                    decoded = base64_decode(c);
+	            debug("sending 'AF' %s to squid with data:\n", cred);
+                    hex_dump(decoded, (strlen(c) * 3) / 4);
+                } else
+                    SEND3("AF %s %s", c, cred);
+            } else {
+                if (Negotiate_packet_debug_enabled) {
+                    printf("TT %s\n",c);
+                    decoded = base64_decode(c);
+	            debug("sending 'TT' to squid with data:\n");
+                    hex_dump(decoded, (strlen(c) * 3) / 4);
+                } else {
+                    SEND2("TT %s", c);
+                }
+                have_serverblob = 1;
+            }
+        } else
+            helperfail("can't obtain server blob");
+	return 1;
+    }
+
+    if (memcmp(buf, "KK ", 3) == 0) {	/* authenticate-request */
+        if (!have_serverblob) {
+	    helperfail("invalid server blob");
+	    return 1;
+        }
+	/* figure out what we got */
+	decoded = base64_decode(buf + 3);
+	/*  Note: we don't need to manage memory at this point, since
+	 *  base64_decode returns a pointer to static storage.
+	 */
+	if (!decoded) {		/* decoding failure, return error */
+	    SEND("NA * Packet format error, couldn't base64-decode");
+	    return 1;
+	}
+
+        /* check against SSPI */
+	plen = (strlen(buf) - 3) * 3 / 4;		/* we only need it here. Optimization */
+        c = (char *) SSP_ValidateNegotiateCredentials(decoded, plen, &Done, &status, cred);
+        
+        if (status == SSP_ERROR) {
+#if FAIL_DEBUG
+            fail_debug_enabled = 1;
+#endif
+            FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 
+                          FORMAT_MESSAGE_IGNORE_INSERTS,
+                          NULL,
+                          GetLastError(),
+                          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),   /* Default language */
+                          (LPTSTR) &ErrorMessage,
+                          0,
+                          NULL);
+            if (ErrorMessage[strlen(ErrorMessage) - 1] == '\n')
+                ErrorMessage[strlen(ErrorMessage) - 1] = '\0';
+            if (ErrorMessage[strlen(ErrorMessage) - 1] == '\r')
+                ErrorMessage[strlen(ErrorMessage) - 1] = '\0';
+	    SEND2("NA * %s", ErrorMessage);
+            LocalFree(ErrorMessage);
+	    return 1;
+	}
+
+        if (Done) {
+	    lc(cred);		/* let's lowercase them for our convenience */
+            have_serverblob = 0;
+            Done = FALSE;
+            if (Negotiate_packet_debug_enabled) {
+                printf("AF %s %s\n",c,cred);
+                decoded = base64_decode(c);
+	        debug("sending 'AF' %s to squid with data:\n", cred);
+                hex_dump(decoded, (strlen(c) * 3) / 4);
+            } else {
+                SEND3("AF %s %s", c, cred);
+            }
+	    return 1;
+        } else {
+            if (Negotiate_packet_debug_enabled) {
+                printf("TT %s\n",c);
+                decoded = base64_decode(c);
+	        debug("sending 'TT' to squid with data:\n");
+                hex_dump(decoded, (strlen(c) * 3) / 4);
+            } else
+               	SEND2("TT %s", c);
+	    return 1;
+        }
+
+    } else {	/* not an auth-request */
+	helperfail("illegal request received");
+	fprintf(stderr, "Illegal request received: '%s'\n", buf);
+	return 1;
+    }
+    helperfail("detected protocol error");
+    return 1;
+/********* END ********/
+}
+
+int
+main(int argc, char *argv[])
+{
+    my_program_name = argv[0];
+
+    process_options(argc, argv);
+
+    debug("%s build " __DATE__ ", " __TIME__ " starting up...\n", my_program_name);
+    
+    if (LoadSecurityDll(SSP_NTLM, NEGOTIATE_PACKAGE_NAME) == NULL) {
+	fprintf(stderr, "FATAL, can't initialize SSPI, exiting.\n");
+	exit(1);
+    }
+    debug("SSPI initialized OK\n");
+
+    atexit(UnloadSecurityDll);
+
+    /* initialize FDescs */
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+
+    while (manage_request()) {
+	/* everything is done within manage_request */
+    }
+    exit(0);
+}
Index: squid/helpers/negotiate_auth/Win32/readme.txt
diff -u /dev/null squid/helpers/negotiate_auth/Win32/readme.txt:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/helpers/negotiate_auth/Win32/readme.txt	Sun Mar 19 08:59:51 2006
@@ -0,0 +1,41 @@
+win32_negotiate_auth.exe
+
+Native Windows Negotiate authenticator for Squid.
+
+=====
+Usage
+=====
+
+win32_negotiate_auth [-d] [-v] [-h]
+
+-d enables debugging.
+-v enables verbose Negotiate packet debugging.
+-h print program usage
+
+This is released under the GNU General Public License
+
+==============
+Allowing Users
+==============
+
+Users that are allowed to access the web proxy must have the Windows NT
+User Rights "logon from the network".
+
+Squid.conf typical minimal required changes:
+
+auth_param negotiate program c:/squid/libexec/win32_negotiate_auth.exe
+auth_param negotiate children 5
+
+acl password proxy_auth REQUIRED
+
+http_access allow password
+http_access deny all
+
+Refer to Squid documentation for more details.
+
+Currently Internet Explorer has some problems with ftp:// URLs when handling
+internal Squid FTP icons. The following squid.conf ACL works around this:
+
+acl internal_icons urlpath_regex -i /squid-internal-static/icons/
+
+http_access allow our_networks internal_icons <== BEFORE authentication ACL !!!
Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c
diff -u squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c:1.1.4.11 squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c:1.1.4.11.2.2
--- squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c:1.1.4.11	Fri Sep 16 00:57:13 2005
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c	Sun Oct 30 05:04:37 2005
@@ -271,7 +271,7 @@
     } else
         debug("checking local user\n");
 
-    rv = SSP_ValidateCredentials(auth, auth_length, credentials);
+    rv = SSP_ValidateNTLMCredentials(auth, auth_length, credentials);
 
     debug("Login attempt had result %d\n", rv);
 
@@ -329,6 +329,9 @@
      * (in a single line of course)
      */
 
+    if (!data)
+        return;
+
     if (debug_enabled) {
         unsigned char *p = data;
         unsigned char c;
Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h
diff -u squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h:1.1.4.9 squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h:1.1.4.9.2.1
--- squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h:1.1.4.9	Fri Sep 16 00:57:13 2005
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h	Sat Oct 22 10:45:23 2005
@@ -40,7 +40,7 @@
 #include <sys/types.h>
 
 extern int debug_enabled;
-#ifdef FAIL_DEBUG
+#if FAIL_DEBUG
 extern int fail_debug_enabled;
 #endif
 
@@ -65,14 +65,18 @@
 {
 #ifdef DEBUG
 #ifdef _SQUID_MSWIN_
+#if FAIL_DEBUG
     if (debug_enabled || fail_debug_enabled) {
+#else
+    if (debug_enabled) {
+#endif
 	va_list args;
 
 	va_start(args,format);
 	fprintf(stderr, "ntlm-auth[%d]: ",getpid());
 	vfprintf(stderr, format, args);
 	va_end(args);
-#ifdef FAIL_DEBUG
+#if FAIL_DEBUG
         fail_debug_enabled = 0;
 #endif
     }
Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c
diff -u squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c:1.1.4.20 squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c:1.1.4.20.2.1
--- squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c:1.1.4.20	Fri Sep 16 00:57:13 2005
+++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c	Sat Oct 22 10:45:23 2005
@@ -76,7 +76,7 @@
 char * NTDisAllowedGroup;
 int UseDisallowedGroup = 0;
 int UseAllowedGroup = 0;
-#ifdef FAIL_DEBUG
+#if FAIL_DEBUG
 int fail_debug_enabled = 0;
 #endif
 
@@ -105,7 +105,7 @@
 void
 helperfail(const char *reason)
 {
-#ifdef FAIL_DEBUG
+#if FAIL_DEBUG
     fail_debug_enabled =1;
 #endif
     SEND2("BH %s", reason);
@@ -342,7 +342,7 @@
 	    cred = ntlm_check_auth((ntlm_authenticate *) decoded, plen);
             have_challenge = 0;
 	    if (cred == NULL) {
-#ifdef FAIL_DEBUG
+#if FAIL_DEBUG
                 fail_debug_enabled =1;
 #endif
 		switch (ntlm_errno) {
@@ -402,7 +402,7 @@
 
     debug("%s build " __DATE__ ", " __TIME__ " starting up...\n", my_program_name);
     
-    if (LoadSecurityDll(SSP_NTLM) == NULL) {
+    if (LoadSecurityDll(SSP_NTLM, NTLM_PACKAGE_NAME) == NULL) {
 	fprintf(stderr, "FATAL, can't initialize SSPI, exiting.\n");
 	exit(1);
     }
Index: squid/include/sspwin32.h
diff -u squid/include/sspwin32.h:1.1.4.4 squid/include/sspwin32.h:1.1.4.4.2.2
--- squid/include/sspwin32.h:1.1.4.4	Sun Nov 30 03:44:38 2003
+++ squid/include/sspwin32.h	Sun Oct 30 05:04:37 2005
@@ -19,6 +19,7 @@
 #define _LIBSSPWIN32_H_
 #define SECURITY_WIN32
 #define NTLM_PACKAGE_NAME "NTLM"
+#define NEGOTIATE_PACKAGE_NAME "Negotiate"
 
 #ifdef _SQUID_CYGWIN_
 #include <wchar.h>
@@ -30,17 +31,28 @@
 #include <security.h>
 #include <sspi.h>
 
+typedef char * SSP_blobP;
+
 #define WINNT_SECURITY_DLL "security.dll"
 #define WIN2K_SECURITY_DLL "secur32.dll"
 
 #define SSP_BASIC 1
 #define SSP_NTLM 2
 
-HMODULE LoadSecurityDll(int);
+#define SSP_MAX_CRED_LEN 848
+
+#define SSP_DEBUG 0
+
+#define SSP_OK 1
+#define SSP_ERROR 2
+
+HMODULE LoadSecurityDll(int, char *);
 void UnloadSecurityDll(void);
 BOOL WINAPI SSP_LogonUser(PTSTR, PTSTR, PTSTR);
-BOOL WINAPI SSP_ValidateCredentials(PVOID, int, char *);
+BOOL WINAPI SSP_ValidateNTLMCredentials(PVOID, int, char *);
+const char * WINAPI SSP_ValidateNegotiateCredentials(PVOID, int, PBOOL, int *, char *);
 const char * WINAPI SSP_MakeChallenge(PVOID, int);
+const char * WINAPI SSP_MakeNegotiateBlob(PVOID, int, PBOOL, int *, char *);
 
 extern BOOL Use_Unicode;
 extern BOOL NTLM_LocalCall;
Index: squid/lib/sspwin32.c
diff -u squid/lib/sspwin32.c:1.1.4.7 squid/lib/sspwin32.c:1.1.4.7.2.2
--- squid/lib/sspwin32.c:1.1.4.7	Sun Nov 30 03:44:38 2003
+++ squid/lib/sspwin32.c	Tue Nov  1 06:35:09 2005
@@ -31,6 +31,7 @@
 
 static HMODULE hModule;
 static int NTLM_mode = SSP_BASIC;
+static char * SSP_Package_InUse;
 SECURITY_STATUS SecurityStatus = SEC_E_OK;
 
 static DWORD cbMaxToken = 0;
@@ -67,6 +68,7 @@
     if (hModule)
 	FreeLibrary(hModule);
 
+    xfree(SSP_Package_InUse);
     xfree(pClientBuf);
     xfree(pServerBuf);
 
@@ -84,7 +86,7 @@
 }
 
 
-HMODULE LoadSecurityDll(int mode) 
+HMODULE LoadSecurityDll(int mode, char * SSP_Package) 
 {
     TCHAR lpszDLL[MAX_PATH];
     OSVERSIONINFO VerInfo;
@@ -199,13 +201,14 @@
     }
 
     /* Get max token size */
-    _QuerySecurityPackageInfo(_T("NTLM"), &pSPI);
+    _QuerySecurityPackageInfo(_T(SSP_Package), &pSPI);
     cbMaxToken = pSPI->cbMaxToken;
     _FreeContextBuffer(pSPI);
 
     /* Allocate buffers for client and server messages */
     pClientBuf = xcalloc(cbMaxToken, sizeof(char));
     pServerBuf = xcalloc(cbMaxToken, sizeof(char));
+    SSP_Package_InUse = xstrdup(SSP_Package);
 
     return hModule;
 }
@@ -232,7 +235,7 @@
     ULONG           fContextAttr;
 
     if (!pAS->fInitialized) {
-	SecurityStatus = _AcquireCredentialsHandle(NULL, _T(NTLM_PACKAGE_NAME), 
+	SecurityStatus = _AcquireCredentialsHandle(NULL, _T(SSP_Package_InUse), 
 	    SECPKG_CRED_OUTBOUND, NULL, (NTLM_mode == SSP_NTLM) ? NULL : pAuthIdentity, NULL, NULL,
 	    &pAS->hcred, &tsExpiry);
 	if (SecurityStatus < 0)
@@ -303,11 +306,18 @@
     SecPkgContext_Names namebuffer;
 
     if (!pAS->fInitialized)  {
-	SecurityStatus = _AcquireCredentialsHandle(NULL, _T("NTLM"), 
+	SecurityStatus = _AcquireCredentialsHandle(NULL, _T(SSP_Package_InUse), 
 	    SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &pAS->hcred, 
 	    &pAS->hcredLifeTime);
-	if (SecurityStatus < 0)
+#if SSP_DEBUG
+        fprintf(stderr, "AcquireCredentialsHandle returned: %x\n", SecurityStatus);
+#endif
+       if (SecurityStatus < 0) {
+#if SSP_DEBUG
+            fprintf(stderr, "AcquireCredentialsHandle failed: %x\n", SecurityStatus);
+#endif
 	    return FALSE;
+        }
 	pAS->fHaveCredHandle = TRUE;
     }
     
@@ -330,28 +340,50 @@
 	pAS->fInitialized ? &pAS->hctxt : NULL, &sbdIn, (NTLM_mode == SSP_NTLM) ? ASC_REQ_DELEGATE : 0, 
 	SECURITY_NATIVE_DREP, &pAS->hctxt, &sbdOut, &fContextAttr, 
 	&pAS->hctxtLifeTime);
-    if (SecurityStatus < 0)
+#if SSP_DEBUG
+    fprintf(stderr, "AcceptSecurityContext returned: %x\n", SecurityStatus);
+#endif
+    if (SecurityStatus < 0) {
+#if SSP_DEBUG
+        fprintf(stderr, "AcceptSecurityContext failed: %x\n", SecurityStatus);
+#endif
 	return FALSE;
+    }
     pAS->fHaveCtxtHandle = TRUE;
     
     /* If necessary, complete token */
     if (SecurityStatus == SEC_I_COMPLETE_NEEDED || SecurityStatus == SEC_I_COMPLETE_AND_CONTINUE) {
 	SecurityStatus = _CompleteAuthToken(&pAS->hctxt, &sbdOut);
-	if (SecurityStatus < 0)
+#if SSP_DEBUG
+        fprintf(stderr, "CompleteAuthToken returned: %x\n", SecurityStatus);
+#endif
+        if (SecurityStatus < 0) {
+#if SSP_DEBUG
+            fprintf(stderr, "CompleteAuthToken failed: %x\n", SecurityStatus);
+#endif
 	    return FALSE;
+        }
     }
 
-    if (credentials != NULL) {
+    if ((credentials != NULL) && 
+         !(SecurityStatus == SEC_I_CONTINUE_NEEDED || SecurityStatus == SEC_I_COMPLETE_AND_CONTINUE)) {
         SecurityStatus = _QueryContextAttributes(&pAS->hctxt, SECPKG_ATTR_NAMES, &namebuffer);
-        if (SecurityStatus < 0)
+#if SSP_DEBUG
+        fprintf(stderr, "QueryContextAttributes returned: %x\n", SecurityStatus);
+#endif
+        if (SecurityStatus < 0) {
+#if SSP_DEBUG
+            fprintf(stderr, "QueryContextAttributes failed: %x\n", SecurityStatus);
+#endif
 	    return FALSE;
-        strcpy(credentials, namebuffer.sUserName);
+        }
+        strncpy(credentials, namebuffer.sUserName, SSP_MAX_CRED_LEN);
     }
 
     *pcbOut = sbOut.cbBuffer;
     if (!pAS->fInitialized)
 	pAS->fInitialized = TRUE;
-    *pfDone = !(SecurityStatus = SEC_I_CONTINUE_NEEDED 
+    *pfDone = !(SecurityStatus == SEC_I_CONTINUE_NEEDED 
 	|| SecurityStatus == SEC_I_COMPLETE_AND_CONTINUE);
     return TRUE;
 }
@@ -474,7 +506,7 @@
 }
 
 
-BOOL WINAPI SSP_ValidateCredentials(PVOID PAutenticateBuf, int AutenticateLen, char * credentials)
+BOOL WINAPI SSP_ValidateNTLMCredentials(PVOID PAutenticateBuf, int AutenticateLen, char * credentials)
 {
     BOOL        fDone      = FALSE;
     BOOL        fResult    = FALSE;
@@ -498,3 +530,65 @@
 
     return fResult;
 }
+
+
+const char * WINAPI SSP_MakeNegotiateBlob(PVOID PNegotiateBuf, int NegotiateLen, PBOOL fDone, int * Status, char * credentials)
+{
+    DWORD       cbOut      = 0;
+    DWORD       cbIn       = 0;
+    const char * encoded = NULL;    
+
+    if (NTLM_asServer.fHaveCtxtHandle)
+	_DeleteSecurityContext(&NTLM_asServer.hctxt);
+    if (NTLM_asServer.fHaveCredHandle)
+        _FreeCredentialsHandle(&NTLM_asServer.hcred);
+
+    memcpy(pClientBuf, PNegotiateBuf, NegotiateLen);
+    ZeroMemory(pServerBuf, cbMaxToken);
+    ZeroMemory(&NTLM_asServer, sizeof(NTLM_asServer));
+    do {
+	if (!hModule)
+	    break;
+
+	/* Prepare server message (challenge) */
+	cbIn = NegotiateLen;
+	cbOut = cbMaxToken;
+	if (!GenServerContext(&NTLM_asServer, pClientBuf, cbIn, pServerBuf, &cbOut, 
+            fDone, credentials)) {
+                *Status = SSP_ERROR;
+                break;
+        }
+	*Status = SSP_OK;
+    } while(0);
+    if (pServerBuf != NULL && cbOut > 0)
+        encoded = base64_encode_bin((char *) pServerBuf, cbOut);
+    return encoded;
+}
+
+
+const char * WINAPI SSP_ValidateNegotiateCredentials(PVOID PAutenticateBuf, int AutenticateLen, PBOOL fDone, int * Status, char * credentials)
+{
+    DWORD       cbOut      = 0;
+    DWORD       cbIn       = 0;
+    const char * encoded = NULL;    
+
+    memcpy(pClientBuf, PAutenticateBuf, AutenticateLen);
+    ZeroMemory(pServerBuf, cbMaxToken);
+    do {
+	if (!hModule)
+	    break;
+
+        /* Prepare server message (authentication) */
+        cbIn = AutenticateLen;
+	cbOut = cbMaxToken;
+	if (!GenServerContext(&NTLM_asServer, pClientBuf, cbIn, pServerBuf, &cbOut, 
+            fDone, credentials)) {
+                *Status = SSP_ERROR;
+                break;
+        }
+	*Status = SSP_OK;
+    } while(0);
+    if (pServerBuf != NULL && cbOut > 0)
+        encoded = base64_encode_bin((char *) pServerBuf, cbOut);
+    return encoded;
+}
Index: squid/port/win32/Makefile.am
diff -u squid/port/win32/Makefile.am:1.1.2.8 squid/port/win32/Makefile.am:1.1.2.8.2.2
--- squid/port/win32/Makefile.am:1.1.2.8	Sun May  1 00:50:11 2005
+++ squid/port/win32/Makefile.am	Sun Oct 30 01:22:28 2005
@@ -41,6 +41,7 @@
 	libheap/libheap.dsp \
 	liblru/liblru.dsp \
 	libmiscutil/libmiscutil.dsp \
+	libnegotiate/libnegotiate.dsp \
 	libntlm/libntlm.dsp \
 	libntlmauth/libntlmauth.dsp \
 	libnull/libnull.dsp \
@@ -48,6 +49,7 @@
 	libsspwin32/libsspwin32.dsp \
 	libufs/libufs.dsp \
 	NCSA_auth/NCSA_auth.dsp \
+	negotiate_auth/negotiate_auth.dsp \
 	nt_auth/nt_auth.dsp \
 	ntlm_win32_auth/ntlm_win32_auth.dsp \
 	pinger/pinger.dsp \
Index: squid/port/win32/squid.dsw
diff -u squid/port/win32/squid.dsw:1.1.2.27 squid/port/win32/squid.dsw:1.1.2.27.2.2
--- squid/port/win32/squid.dsw:1.1.2.27	Sun May  1 00:50:11 2005
+++ squid/port/win32/squid.dsw	Sun Oct 30 01:22:28 2005
@@ -56,6 +56,9 @@
     Begin Project Dependency
     Project_Dep_Name doc
     End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name negotiate_auth
+    End Project Dependency
 }}}
 
 ###############################################################################
@@ -348,6 +351,21 @@
 
 ###############################################################################
 
+Project: "libnegotiate"=".\libnegotiate\libnegotiate.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name modules
+    End Project Dependency
+}}}
+
+###############################################################################
+
 Project: "libntlm"=".\libntlm\libntlm.dsp" - Package Owner=<4>
 
 Package=<5>
@@ -447,6 +465,24 @@
 
 ###############################################################################
 
+Project: "negotiate_auth"=".\negotiate_auth\negotiate_auth.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name libmiscutil
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name libsspwin32
+    End Project Dependency
+}}}
+
+###############################################################################
+
 Project: "nt_auth"=".\nt_auth\nt_auth.dsp" - Package Owner=<4>
 
 Package=<5>
@@ -557,6 +593,9 @@
     Begin Project Dependency
     Project_Dep_Name libnull
     End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name libnegotiate
+    End Project Dependency
 }}}
 
 ###############################################################################
Index: squid/port/win32/squid_mswin.mak
diff -u squid/port/win32/squid_mswin.mak:1.1.2.9 squid/port/win32/squid_mswin.mak:1.1.2.9.2.1
--- squid/port/win32/squid_mswin.mak:1.1.2.9	Sun Oct  2 08:22:21 2005
+++ squid/port/win32/squid_mswin.mak	Sat Oct  8 03:01:45 2005
@@ -46,7 +46,7 @@
 DEFAULT_MIB_PATH	= $(datadir)/mib.txt
 DEFAULT_HOSTS		= none
 
-AUTH_MODULES = basic ntlm digest
+AUTH_MODULES = basic ntlm digest negotiate
 REPL_POLICIES = lru heap
 STORE_MODULES = ufs awin32 null
 
Index: squid/port/win32/update.cmd
diff -u squid/port/win32/update.cmd:1.1.2.27 squid/port/win32/update.cmd:1.1.2.26.2.3
--- squid/port/win32/update.cmd:1.1.2.27	Tue Nov  8 06:44:26 2005
+++ squid/port/win32/update.cmd	Sat Mar 25 05:09:57 2006
@@ -21,6 +21,7 @@
 copy %0\..\nt_auth\%1\nt_auth.exe %2\libexec\win32_auth.exe
 copy %0\..\ncsa_auth\%1\ncsa_auth.exe %2\libexec\ncsa_auth.exe
 copy %0\..\ntlm_win32_auth\%1\ntlm_win32_auth.exe %2\libexec\win32_ntlm_auth.exe
+copy %0\..\negotiate_auth\%1\negotiate_auth.exe %2\libexec\win32_negotiate_auth.exe
 copy %0\..\ldap_auth\%1\ldap_auth.exe %2\libexec\squid_ldap_auth.exe
 copy %0\..\ldap_group\%1\ldap_group.exe %2\libexec\squid_ldap_group.exe
 copy %0\..\win32_check_group\%1\win32_check_group.exe %2\libexec\win32_check_group.exe
@@ -39,6 +40,7 @@
 
 copy %0\..\readme.txt %2\docs\readme.txt
 copy %0\..\..\..\helpers\ntlm_auth\NTLMSSP-WIN32\readme.txt %2\docs\win32_ntlm_auth.txt
+copy %0\..\..\..\helpers\negotiate_auth\Win32\readme.txt %2\docs\win32_negotiate_auth.txt
 copy %0\..\..\..\helpers\external_acl\win32_group\readme.txt %2\docs\win32_check_group.txt
 copy %0\..\..\..\helpers\external_acl\ip_user\README %2\docs\ip_user_check.txt
 copy %0\..\..\..\helpers\basic_auth\win32_locallogon\readme.txt %2\docs\win32_auth.txt
Index: squid/port/win32/libnegotiate/.cvsignore
diff -u /dev/null squid/port/win32/libnegotiate/.cvsignore:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/port/win32/libnegotiate/.cvsignore	Sat Oct  8 03:01:46 2005
@@ -0,0 +1,3 @@
+.cvsignore
+Debug
+Release
Index: squid/port/win32/libnegotiate/libnegotiate.dsp
diff -u /dev/null squid/port/win32/libnegotiate/libnegotiate.dsp:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/port/win32/libnegotiate/libnegotiate.dsp	Sat Oct  8 03:01:46 2005
@@ -0,0 +1,100 @@
+# Microsoft Developer Studio Project File - Name="libnegotiate" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=libnegotiate - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "libnegotiate.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "libnegotiate.mak" CFG="libnegotiate - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "libnegotiate - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "libnegotiate - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "libnegotiate - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /G6 /MT /W3 /GX /O2 /I "../../../include" /I "../../../src" /I "../include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "HAVE_CONFIG_H" /D _FILE_OFFSET_BITS=64 /YX /FD /c
+# ADD BASE RSC /l 0x410 /d "NDEBUG"
+# ADD RSC /l 0x410 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "libnegotiate - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ  /c
+# ADD CPP /nologo /G6 /MTd /W3 /Gm /GX /ZI /Od /I "../../../include" /I "../../../src" /I "../include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "HAVE_CONFIG_H" /D _FILE_OFFSET_BITS=64 /YX /FD /GZ  /c
+# ADD BASE RSC /l 0x410 /d "_DEBUG"
+# ADD RSC /l 0x410 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "libnegotiate - Win32 Release"
+# Name "libnegotiate - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\..\..\src\auth\negotiate\auth_negotiate.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\..\..\src\auth\negotiate\auth_negotiate.h
+# End Source File
+# End Group
+# End Target
+# End Project
Index: squid/port/win32/negotiate_auth/.cvsignore
diff -u /dev/null squid/port/win32/negotiate_auth/.cvsignore:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/port/win32/negotiate_auth/.cvsignore	Sun Oct 30 01:22:28 2005
@@ -0,0 +1,3 @@
+.cvsignore
+Debug
+Release
Index: squid/port/win32/negotiate_auth/negotiate_auth.dsp
diff -u /dev/null squid/port/win32/negotiate_auth/negotiate_auth.dsp:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/port/win32/negotiate_auth/negotiate_auth.dsp	Sun Oct 30 01:22:28 2005
@@ -0,0 +1,110 @@
+# Microsoft Developer Studio Project File - Name="negotiate_auth" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=negotiate_auth - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "negotiate_auth.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "negotiate_auth.mak" CFG="negotiate_auth - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "negotiate_auth - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "negotiate_auth - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "negotiate_auth - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /G6 /MT /W3 /GX /O2 /I "../../../include" /I "../../../src" /I "../include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _FILE_OFFSET_BITS=64 /YX /FD /c
+# ADD BASE RSC /l 0x410 /d "NDEBUG"
+# ADD RSC /l 0x410 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 Netapi32.lib Advapi32.lib ws2_32.lib psapi.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF  "$(CFG)" == "negotiate_auth - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /G6 /MTd /W3 /Gm /GX /ZI /Od /I "../../../include" /I "../../../src" /I "../include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _FILE_OFFSET_BITS=64 /YX /FD /GZ /c
+# ADD BASE RSC /l 0x410 /d "_DEBUG"
+# ADD RSC /l 0x410 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 Netapi32.lib Advapi32.lib ws2_32.lib psapi.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "negotiate_auth - Win32 Release"
+# Name "negotiate_auth - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\..\..\helpers\negotiate_auth\win32\libnegotiatessp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\helpers\negotiate_auth\win32\negotiate_auth.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\..\..\helpers\negotiate_auth\win32\negotiate.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
Index: squid/src/authenticate.c
diff -u squid/src/authenticate.c:1.23.2.1.4.14 squid/src/authenticate.c:1.23.2.1.4.14.2.1
--- squid/src/authenticate.c:1.23.2.1.4.14	Mon Sep 19 09:23:07 2005
+++ squid/src/authenticate.c	Sat Oct  8 02:13:11 2005
@@ -763,11 +763,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
diff -u squid/src/cf.data.pre:1.49.2.5.4.83 squid/src/cf.data.pre:1.49.2.5.4.80.2.5
--- squid/src/cf.data.pre:1.49.2.5.4.83	Sun Feb 26 01:14:42 2006
+++ squid/src/cf.data.pre	Sun Feb 26 01:52:30 2006
@@ -1337,11 +1337,11 @@
 	auth_param basic program @DEFAULT_PREFIX@/libexec/ncsa_auth @DEFAULT_PREFIX@/etc/passwd
 
 	"children" numberofchildren
-	The number of authenticator processes to spawn.
-	If you start too few Squid will have to wait for them to process a
-	backlog of usercode/password verifications, slowing it down. When
-	password verifications are done via a (slow) network you are likely to
-	need lots of authenticator processes.
+	The number of authenticator processes to spawn. If you start too few 
+	squid will have to wait for them to process a backlog of credential
+	verifications, slowing it down. When credential verifications are
+	done via a (slow) network you are likely to need lots of
+	authenticator processes.
 	auth_param basic children 5
 
 	"realm" realmstring
@@ -1393,11 +1393,11 @@
 
 
 	"children" numberofchildren
-	The number of authenticator processes to spawn (no default). If you
-	start too few Squid will have to wait for them to process a backlog of
-	H(A1) calculations, slowing it down.  When the H(A1) calculations are
-	done via a (slow) network you are likely to need lots of authenticator
-	processes.
+	The number of authenticator processes to spawn. If you start too few 
+	squid will have to wait for them to process a backlog of credential
+	verifications, slowing it down. When credential verifications are
+	done via a (slow) network you are likely to need lots of
+	authenticator processes.
 	auth_param digest children 5
 
 	"realm" realmstring
@@ -1454,27 +1454,13 @@
 	auth_param ntlm program /path/to/samba/bin/ntlm_auth --helper-protocol=squid-2.5-ntlmssp
 
 	"children" numberofchildren
-	The number of authenticator processes to spawn (no default). If you
-	start too few Squid will have to wait for them to process a backlog
-	of credential verifications, slowing it down. When credential
-	verifications are done via a (slow) network you are likely to need
-	lots of authenticator processes.
+	The number of authenticator processes to spawn. If you start too few 
+	squid will have to wait for them to process a backlog of credential
+	verifications, slowing it down. When credential verifications are
+	done via a (slow) network you are likely to need 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
@@ -1489,29 +1475,67 @@
 	all users must be allowed to log on the proxy servers too, or they'll
 	get "invalid workstation" errors - and access denied - when trying to
 	use Squid's services.
-	Use of ntlm NEGOTIATE is incompatible with challenge reuse, so
-	enabling this parameter will OVERRIDE the max_challenge_reuses and
-	max_challenge_lifetime parameters and set them to 0.
-	auth_param ntlm use_ntlm_negotiate off
+	auth_param ntlm use_ntlm_negotiate on
+
+	"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 on
+
+	=== Negotiate scheme options follow ===
+
+	"program" cmdline
+	Specify the command for the external Negotiate 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-4.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
+
+	"children" numberofchildren
+	The number of authenticator processes to spawn. If you start too few 
+	squid will have to wait for them to process a backlog of credential
+	verifications, slowing it down. When credential verifications are
+	done via a (slow) network you are likely to need lots of
+	authenticator processes.
+	auth_param negotiate children 5
+
+	"keep_alive" on|off
+	If you experience problems with PUT/POST requests when using the
+	Negotiate authentication scheme then you can try setting this to
+	off. This will cause Squid to forcibly close the connection on
+	the initial requests where the browser asks which schemes are
+	supported by the proxy.
+
+	auth_param negotiate keep_alive on
 
 NOCOMMENT_START
-#Recommended minimum configuration:
+#Recommended minimum configuration per scheme:
+#auth_param negotiate program <uncomment and complete this line to activate>
+#auth_param negotiate children 5
+#auth_param negotiate keep_alive on
+#auth_param ntlm program <uncomment and complete this line to activate>
+#auth_param ntlm children 5
+#auth_param ntlm use_ntlm_negotiate on
+#auth_param ntlm keep_alive on
 #auth_param digest program <uncomment and complete this line>
 #auth_param digest children 5
 #auth_param digest realm Squid proxy-caching web server
 #auth_param digest nonce_garbage_interval 5 minutes
 #auth_param digest nonce_max_duration 30 minutes
 #auth_param digest nonce_max_count 50
-#auth_param ntlm program <uncomment and complete this line to activate>
-#auth_param ntlm children 5
-#auth_param ntlm max_challenge_reuses 0
-#auth_param ntlm max_challenge_lifetime 2 minutes
-#auth_param ntlm use_ntlm_negotiate off
 #auth_param basic program <uncomment and complete this line>
-auth_param basic children 5
-auth_param basic realm Squid proxy-caching web server
-auth_param basic credentialsttl 2 hours
-auth_param basic casesensitive off
+#auth_param basic children 5
+#auth_param basic realm Squid proxy-caching web server
+#auth_param basic credentialsttl 2 hours
+#auth_param basic casesensitive off
 NOCOMMENT_END
 DOC_END
 
Index: squid/src/enums.h
diff -u squid/src/enums.h:1.29.6.18 squid/src/enums.h:1.29.6.17.2.2
--- squid/src/enums.h:1.29.6.18	Sat Nov 12 00:43:36 2005
+++ squid/src/enums.h	Sat Nov 12 00:47:42 2005
@@ -534,6 +534,7 @@
     AUTH_BASIC,
     AUTH_NTLM,
     AUTH_DIGEST,
+    AUTH_NEGOTIATE,
     AUTH_BROKEN			/* known type, but broken data */
 } auth_type_t;
 
Index: squid/src/helper.c
diff -u squid/src/helper.c:1.16.10.24 squid/src/helper.c:1.16.10.23.2.3
--- squid/src/helper.c:1.16.10.24	Sat Mar 11 06:07:46 2006
+++ squid/src/helper.c	Sat Mar 11 08:58:58 2006
@@ -416,6 +416,8 @@
     helper_stateful_server *srv;
     dlink_node *link;
     double tt;
+    storeAppendPrintf(sentry, "program: %s\n",
+	hlp->cmdline->key);
     storeAppendPrintf(sentry, "number running: %d of %d\n",
 	hlp->n_running, hlp->n_to_start);
     storeAppendPrintf(sentry, "requests sent: %d\n",
@@ -741,6 +743,8 @@
     } else if ((t = strchr(srv->buf, '\n'))) {
 	/* end of reply found */
 	debug(84, 3) ("helperHandleRead: end of reply found\n");
+	if (t > srv->buf && t[-1] == '\r')
+	    t[-1] = '\0';
 	*t = '\0';
 	srv->flags.busy = 0;
 	srv->offset = 0;
@@ -798,6 +802,8 @@
     } else if ((t = strchr(srv->buf, '\n'))) {
 	/* end of reply found */
 	debug(84, 3) ("helperStatefulHandleRead: end of reply found\n");
+	if (t > srv->buf && t[-1] == '\r')
+	    t[-1] = '\0';
 	*t = '\0';
 	srv->flags.busy = 0;
 	srv->offset = 0;
Index: squid/src/auth/Makefile.am
diff -u squid/src/auth/Makefile.am:1.2.40.1 squid/src/auth/Makefile.am:1.2.40.1.2.1
--- squid/src/auth/Makefile.am:1.2.40.1	Sun Oct 27 02:46:15 2002
+++ squid/src/auth/Makefile.am	Sat Oct  8 02:13:11 2005
@@ -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
 
 if ENABLE_WIN32SPECIFIC
 INCLUDES      = -I. -I$(top_srcdir)/port/win32/include -I$(top_srcdir)/include \
Index: squid/src/auth/negotiate/.cvsignore
diff -u /dev/null squid/src/auth/negotiate/.cvsignore:1.1.2.1
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/auth/negotiate/.cvsignore	Sat Oct  8 02:24:12 2005
@@ -0,0 +1,2 @@
+.cvsignore
+Makefile.in
Index: squid/src/auth/negotiate/Makefile.am
diff -u /dev/null squid/src/auth/negotiate/Makefile.am:1.1.6.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/auth/negotiate/Makefile.am	Sat Oct  8 02:24:12 2005
@@ -0,0 +1 @@
+SUBDIRS =
Index: squid/src/auth/negotiate/auth_negotiate.c
diff -u /dev/null squid/src/auth/negotiate/auth_negotiate.c:1.1.6.4
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/auth/negotiate/auth_negotiate.c	Sat Nov  5 01:11:04 2005
@@ -0,0 +1,758 @@
+
+/*
+ * $Id$
+ *
+ * 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);
+    safe_free(negotiateConfig);
+    scheme->scheme_data = 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 program", name, "negotiate");
+    while (list != NULL) {
+	storeAppendPrintf(entry, " %s", list->key);
+	list = list->next;
+    }
+    storeAppendPrintf(entry, "\n");
+    storeAppendPrintf(entry, "%s %s children %d\n", name, "negotiate", config->authenticateChildren);
+    storeAppendPrintf(entry, "%s %s keep_alive %s\n", name, "negotiate", config->keep_alive ? "on" : "off");
+}
+
+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);
+    }
+}
+
+
+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) {
+	/*
+	 * 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) {
+	    debug(28, 1) ("pipeline prefetching incompatile with Negotiate authentication. Disabling pipeline_prefetch\n");
+	    Config.onoff.pipeline_prefetch = 0;
+	}
+	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 -2;
+    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 (!request->flags.proxy_keepalive)
+	return;
+    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);
+	request->flags.must_keepalive = 1;
+	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);
+    safe_free(negotiate_request->client_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");
+    safe_free(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';
+	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("Authentication 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(blob);
+	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;
+}
Index: squid/src/auth/negotiate/auth_negotiate.h
diff -u /dev/null squid/src/auth/negotiate/auth_negotiate.h:1.1.6.2
--- /dev/null		Thu Jan  1 01:00:00 1970
+++ squid/src/auth/negotiate/auth_negotiate.h	Sat Oct  8 02:24:12 2005
@@ -0,0 +1,58 @@
+/*
+ * 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 _auth_negotiate_config auth_negotiate_config;
+
+#endif
Index: squid/src/auth/ntlm/auth_ntlm.c
diff -u squid/src/auth/ntlm/auth_ntlm.c:1.18.2.1.4.25 squid/src/auth/ntlm/auth_ntlm.c:1.18.2.1.4.24.2.7
--- squid/src/auth/ntlm/auth_ntlm.c:1.18.2.1.4.25	Sun Feb 26 01:14:43 2006
+++ squid/src/auth/ntlm/auth_ntlm.c	Sun Feb 26 01:52:32 2006
@@ -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);
@@ -138,8 +123,8 @@
     assert(ntlmConfig == scheme->scheme_data);
     if (ntlmConfig->authenticate)
 	wordlistDestroy(&ntlmConfig->authenticate);
-    xfree(ntlmConfig);
-    ntlmConfig = NULL;
+    safe_free(ntlmConfig);
+    scheme->scheme_data = NULL;
 }
 
 static void
@@ -147,17 +132,15 @@
 {
     auth_ntlm_config *config = scheme->scheme_data;
     wordlist *list = config->authenticate;
-    storeAppendPrintf(entry, "%s %s", name, "ntlm");
+    storeAppendPrintf(entry, "%s %s program", name, "ntlm");
     while (list != NULL) {
 	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,7 @@
 	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->keep_alive = 1;
     }
     ntlmConfig = scheme->scheme_data;
     if (strcasecmp(param_str, "program") == 0) {
@@ -182,10 +163,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 {
@@ -216,7 +195,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
@@ -237,21 +215,6 @@
 	    debug(28, 1) ("pipeline prefetching incompatile with NTLM authentication. Disabling pipeline_prefetch\n");
 	    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;
-	}
-    /* Native Windows helpers don't allow Challenge reuse even when
-     * running without NTLM NEGOTIATE packet support.
-     */
-#if defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_)
-    if (ntlmConfig->challengeuses > 0) {
-	debug(28, 1) ("challenge reuses incompatible with Windows platform. Disabling challenge reuse\n");
-        ntlmConfig->challengeuses = 0;
-    }
-#endif
-	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)
@@ -259,27 +222,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",
@@ -300,12 +246,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 */
@@ -315,19 +269,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 */
-    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 */
+    case AUTHENTICATE_STATE_NEGOTIATE:	/* send to client */
+	assert(ntlm_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) ("authenticateNTLMDirection: Unexpected AUTHENTICATE_STATE_INITIAL\n");
+	return -2;
     }
     return -2;
 }
@@ -341,41 +299,45 @@
 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);
-		request->flags.must_keepalive = 1;
-		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);
+	request->flags.must_keepalive = 1;
+	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);
+	httpHeaderPutStrf(&rep->header, type, "NTLM");
+	break;
+    default:
+	debug(29, 0) ("authenticateNTLMFixErrorHeader: state %d.\n", ntlm_request->auth_state);
+	fatal("unexpected state in AuthenticateNTLMFixErrorHeader.\n");
     }
 }
 
@@ -384,12 +346,8 @@
 {
     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);
+    safe_free(ntlm_request->client_blob);
     if (ntlm_request->authserver != NULL) {
 	debug(29, 9) ("authenticateNTLMRequestFree: releasing server '%p'\n", ntlm_request->authserver);
 	authenticateNTLMReleaseServer(ntlm_request);
@@ -405,20 +363,13 @@
     auth_user_request->scheme_data = NULL;
 }
 
-static void authenticateNTLMChallengeCacheRemoveLink(ntlm_challenge_hash_pointer * challenge_hash);
-
 static void
 authenticateNTLMFreeUser(auth_user_t * auth_user)
 {
     ntlm_user_t *ntlm_user = auth_user->scheme_data;
 
     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);
-    }
+    safe_free(ntlm_user->username);
     memPoolFree(ntlm_user_pool, ntlm_user);
     auth_user->scheme_data = NULL;
 }
@@ -428,60 +379,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) {
@@ -491,113 +405,69 @@
 	return;
     }
     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");
+	debug(29, 1) ("AuthenticateNTLMHandleReply: Helper '%p' crashed!.\n", srv);
+	reply = (char *)"BH Internal error";
     }
-    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);
-    if (!ntlm_request->authserver)
+
+    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 */
+    blob = strchr(reply, ' ');
+    if (blob) {
+	blob++;
+    }
+    
     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);
-	ntlm_request->authchallenge = xstrdup(reply);
-	helperstate->challengeuses = 1;
+	/* 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_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) {
 	/* 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);
-	/* BH code: mark helper as broken */
-	authenticateNTLMResetServer(ntlm_request);
-	debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
-#endif
+	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", blob);
+	authenticateNTLMReleaseServer(ntlm_request);
+	ntlm_request->auth_state = AUTHENTICATE_STATE_FINISHED;
     } else if (strncasecmp(reply, "NA ", 3) == 0) {
-	/* todo: action of Negotiate state on error */
-	ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
-	authenticateNTLMResetServer(ntlm_request);
-	debug(29, 4) ("authenticateNTLMHandleReply: Error validating user via NTLM. Error returned '%s'\n", reply);
-	reply += 3;
 	safe_free(auth_user_request->message);
-	if (*reply)
-	    auth_user_request->message = xstrdup(reply);
+	auth_user_request->message = xstrdup(blob);
+	ntlm_request->auth_state = AUTHENTICATE_STATE_FAILED;
+	authenticateNTLMReleaseServer(ntlm_request);
+	debug(29, 4) ("authenticateNTLMHandleReply: Failed validating user via NTLM. Error returned '%s'\n", blob);
     } else if (strncasecmp(reply, "BH ", 3) == 0) {
 	/* TODO kick off a refresh process. This can occur after a YR or after
 	 * a KK. If after a YR release the helper and resubmit the request via 
 	 * Authenticate NTLM start. 
 	 * If after a KK deny the user's request w/ 407 and mark the helper as 
 	 * 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));
-	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;
-	    reply += 3;
-	    safe_free(auth_user_request->message);
-	    if (*reply)
-		auth_user_request->message = xstrdup(reply);
-	}
+	auth_user_request->message = xstrdup(blob);
+	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);
     }
@@ -613,48 +483,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;
@@ -669,26 +502,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);
@@ -697,120 +513,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");
+    } 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 */
@@ -848,22 +569,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
@@ -872,7 +579,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));
@@ -881,7 +588,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;
 }
@@ -893,47 +601,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;
@@ -942,19 +614,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;
@@ -966,13 +634,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);
+    blob = proxy_auth;
+    while (xisspace(*blob) && *blob)
+	blob++;
+    while (!xisspace(*blob) && *blob)
+	blob++;
+    while (xisspace(*blob) && *blob)
+	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 */
@@ -980,68 +668,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. */
-	ntlm_request->ntlmauthenticate = xstrdup(proxy_auth);
-	/* 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);
@@ -1060,17 +704,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
diff -u squid/src/auth/ntlm/auth_ntlm.h:1.8.10.4 squid/src/auth/ntlm/auth_ntlm.h:1.8.10.4.2.3
--- squid/src/auth/ntlm/auth_ntlm.h:1.8.10.4	Tue Feb 22 04:59:28 2005
+++ squid/src/auth/ntlm/auth_ntlm.h	Tue Nov  8 10:32:53 2005
@@ -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,62 +27,33 @@
 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;
 
 #endif
