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 + * 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 +#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 + * 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 +#include +#include +#include "ntlmauth.h" +#undef debug + +/************* CONFIGURATION ***************/ +/* + * define this if you want debugging + */ +#ifndef DEBUG +#define DEBUG +#endif + +#define FAIL_DEBUG 0 + +/************* END CONFIGURATION ***************/ + +#include + +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 +#include +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 + * Acme Consulting S.r.l., Italy + * + * 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 +#endif +#include "negotiate.h" +#if HAVE_CTYPE_H +#include +#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 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 @@ -30,17 +31,28 @@ #include #include +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 +#auth_param negotiate children 5 +#auth_param negotiate keep_alive on +#auth_param ntlm program +#auth_param ntlm children 5 +#auth_param ntlm use_ntlm_negotiate on +#auth_param ntlm keep_alive on #auth_param digest program #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 -#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 -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 : ""); + 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 : ""); 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